Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generated by `make gen` (buf generate) — reproducible, not committed.
/gen/
# Build artifacts.
/build/
63 changes: 63 additions & 0 deletions agent/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# AlliCodes / TheLookout — agent layer build orchestration.
# Single non-recursive Makefile: proto -> gen -> go -> (native C) one DAG.
SHELL := /bin/bash
.SHELLFLAGS := -eu -o pipefail -c
.DEFAULT_GOAL := all

GO ?= go
BUF ?= buf
CC ?= gcc
CFLAGS ?= -std=c11 -O2 -Wall -Wextra -Werror -pedantic

GEN_DIR := gen
BUILD_DIR := build
STAMP := $(BUILD_DIR)/.stamps
ARCHIVE := $(HOME)/os/bin/archive.sh

PROTOS := $(shell find proto -name '*.proto' 2>/dev/null)
GO_SRC := $(shell find . -name '*.go' -not -path './$(GEN_DIR)/*' 2>/dev/null)
C_SRC := $(wildcard native/*.c)
C_OBJ := $(patsubst native/%.c,$(BUILD_DIR)/%.o,$(C_SRC))

.PHONY: all gen build lint test package clean

all: build

# NOTE: dir names "build" and "gen" deliberately have NO named mkdir target —
# that would collide with the phony "build"/"gen" aggregators (circular dep).
# Each recipe runs `mkdir -p` itself (idempotent).

# ---- 1) proto codegen (stamp: buf outputs aren't 1:1 with .proto) ----
$(STAMP)/gen.stamp: $(PROTOS) buf.gen.yaml buf.yaml
@mkdir -p $(STAMP) $(GEN_DIR)
$(BUF) generate
@touch $@
gen: $(STAMP)/gen.stamp

# ---- 2) Go build (depends on generated code) ----
$(BUILD_DIR)/server: $(STAMP)/gen.stamp $(GO_SRC)
@mkdir -p $(BUILD_DIR)
[ -f go.mod ] && $(GO) build -o $@ ./cmd/server || echo "skip: no go.mod yet"

# ---- 3) native C IPC daemon (plain gcc, POSIX-only, no node-gyp) ----
$(BUILD_DIR)/%.o: native/%.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/ipcd: $(C_OBJ)
$(CC) $(CFLAGS) -o $@ $^

build: gen
@echo "build: proto generated; go/native targets build when sources exist"

# ---- lint / test / package ----
lint: $(STAMP)/gen.stamp
$(BUF) lint
[ -f go.mod ] && $(GO) vet ./... || true
test: build
[ -f go.mod ] && $(GO) test ./... || true
package: build test
@echo "package: TODO (.vsix / service binary)"

# no-rm policy: archive build artifacts instead of deleting
clean:
@[ -x "$(ARCHIVE)" ] && $(ARCHIVE) $(BUILD_DIR) $(GEN_DIR) 2>/dev/null || echo "nothing to archive"
16 changes: 16 additions & 0 deletions agent/buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Local plugins only (no BSR / remote codegen) — offline, dependency-honest.
# Install: go install google.golang.org/protobuf/cmd/protoc-gen-go@<pinned>
# Runtime dep of generated code: google.golang.org/protobuf only.
version: v2
clean: true
managed:
enabled: true
override:
- file_option: go_package_prefix
value: github.com/hellojade-ai/allicodes/agent/gen
plugins:
- local: protoc-gen-go
out: gen
opt: paths=source_relative
inputs:
- directory: proto
9 changes: 9 additions & 0 deletions agent/buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
breaking:
use:
- FILE
5 changes: 5 additions & 0 deletions agent/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/hellojade-ai/allicodes/agent

go 1.26.3

require google.golang.org/protobuf v1.36.11
4 changes: 4 additions & 0 deletions agent/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
64 changes: 64 additions & 0 deletions agent/poc/agent-rpc-client.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env node
/*---------------------------------------------------------------------------------------------
* Phase 3 PoC step 2 — external agent RPC client.
*
* Proves the full path: this external process -> AlliCodes main IPC socket ->
* active window -> ICommandService/IConfigurationService -> core.
*
* Reuses AlliCodes' own built IPC client (out/) so we speak the exact wire
* protocol the `lookoutvs` CLI uses — no hand-rolled framing. Run AFTER a
* build (out/ must exist).
*
* Usage:
* node agent-rpc-client.mjs config.updateValue '{"key":"editor.fontSize","value":15}'
* node agent-rpc-client.mjs command.execute '{"commandId":"workbench.action.toggleSidebarVisibility"}'
*--------------------------------------------------------------------------------------------*/

import { createHash } from 'node:crypto';
import { join } from 'node:path';
import { homedir } from 'node:os';
import { pathToFileURL } from 'node:url';

const REPO = new URL('../../', import.meta.url).pathname; // .../thelookout/
const USER_DATA = join(homedir(), '.config', 'TheLookout');
const VERSION = '1.122.0';

// Reconstruct the main IPC socket path (mirrors createStaticIPCHandle).
function mainIPCHandle() {
const scope = createHash('sha256').update(USER_DATA).digest('hex').slice(0, 8);
const v = VERSION.slice(0, 4);
const runtime = process.env['XDG_RUNTIME_DIR'];
if (process.platform !== 'darwin' && runtime && !process.env['VSCODE_PORTABLE']) {
return join(runtime, `vscode-${scope}-${v}-main.sock`);
}
return join(USER_DATA, `${v}-main.sock`);
}

async function main() {
const [method, paramsJson] = process.argv.slice(2);
if (!method) {
console.error('usage: agent-rpc-client.mjs <method> [paramsJson]');
process.exit(2);
}
const params = paramsJson ? JSON.parse(paramsJson) : {};

// Import AlliCodes' built IPC client.
const ipcNet = await import(pathToFileURL(join(REPO, 'out/vs/base/parts/ipc/node/ipc.net.js')).href);

const handle = mainIPCHandle();
console.error(`[poc] connecting to ${handle}`);
const client = await ipcNet.connect(handle, 'thelookout-agent-poc');
const channel = client.getChannel('agentControl');

console.error(`[poc] -> executeRpc(${method}, ${JSON.stringify(params)})`);
const result = await channel.call('executeRpc', [method, params]);
console.log(JSON.stringify(result, null, 2));

client.dispose();
process.exit(0);
}

main().catch(err => {
console.error('[poc] error:', err?.message ?? err);
process.exit(1);
});
62 changes: 62 additions & 0 deletions agent/proto/ide/agent/v1/agent.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
syntax = "proto3";

package ide.agent.v1;

import "ide/common/v1/common.proto";

// Top-level agent session + the permission gate that fronts every dangerous
// IDE-control op (ENHANCEMENT from the core safety research).
//
// Permission ordering (per IDialogService + workspace-trust research):
// 1. IWorkspaceTrustManagementService.isWorkspaceTrusted / requestWorkspaceTrust
// 2. per-capability policy (this service)
// 3. IDialogService.confirm/prompt with "don't ask again"
// The agent model layer (provide-info + stream-response) deliberately mirrors
// VS Code's LanguageModelChatProvider shape so it stays Copilot/Cursor-compatible.
service AgentService {
// Request permission for a capability before invoking another IDE service.
rpc RequestPermission(RequestPermissionRequest) returns (RequestPermissionResponse);

// Check whether the workspace is trusted; optionally prompt for trust.
rpc CheckWorkspaceTrust(CheckWorkspaceTrustRequest) returns (CheckWorkspaceTrustResponse);

// Open / track an agent session (multi-session cockpit, maps to TheLookout
// RequestService/TaskService model).
rpc StartSession(StartSessionRequest) returns (StartSessionResponse);
}

// Coarse capability the agent is asking to use, for the permission gate.
enum Capability {
CAPABILITY_UNSPECIFIED = 0;
CAPABILITY_INSTALL_EXTENSION = 1;
CAPABILITY_WRITE_SETTINGS = 2;
CAPABILITY_EDIT_FILES = 3;
CAPABILITY_RUN_TERMINAL = 4;
CAPABILITY_CONTROL_WINDOW = 5;
CAPABILITY_RUN_SANDBOX = 6;
}

message RequestPermissionRequest {
Capability capability = 1;
// Human-readable detail shown in the confirmation dialog.
string detail = 2;
}
message RequestPermissionResponse {
ide.common.v1.PermissionDecision decision = 1;
}

message CheckWorkspaceTrustRequest {
// If untrusted and this is true, prompt the user (requestWorkspaceTrust).
bool prompt_if_untrusted = 1;
}
message CheckWorkspaceTrustResponse {
bool trusted = 1;
}

message StartSessionRequest {
string title = 1;
string workspace_uri = 2;
}
message StartSessionResponse {
string session_id = 1;
}
39 changes: 39 additions & 0 deletions agent/proto/ide/command/v1/command.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
syntax = "proto3";

package ide.command.v1;

import "google/protobuf/struct.proto";

// Arbitrary command execution. Backed by ICommandService.executeCommand and
// CommandsRegistry.getCommands (platform/commands/common/commands.ts).
// executeCommand reaches nearly every UI action (workbench.action.*,
// editor.action.*) — the broadest single control surface.
service CommandService {
// executeCommand(id, ...args). Result is JSON-shaped (commands return anything).
rpc Execute(ExecuteRequest) returns (ExecuteResponse);

// getCommands(): enumerate registered commands — the agent's "what can I do" list.
rpc ListCommands(ListCommandsRequest) returns (ListCommandsResponse);
}

message ExecuteRequest {
string command_id = 1;
// Positional args, each an arbitrary JSON value.
repeated google.protobuf.Value args = 2;
}
message ExecuteResponse {
google.protobuf.Value result = 1;
}

message CommandInfo {
string id = 1;
// JSON schema of the command's args, when the command declares metadata.
google.protobuf.Struct args_schema = 2;
}
message ListCommandsRequest {
// If true, exclude internal/underscore-prefixed commands.
bool filter_internal = 1;
}
message ListCommandsResponse {
repeated CommandInfo commands = 1;
}
50 changes: 50 additions & 0 deletions agent/proto/ide/common/v1/common.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
syntax = "proto3";

package ide.common.v1;

// Shared types for the AlliCodes / TheLookout agent IDE-control surface.
// Every service RPC maps to a verified VS Code core service method; the
// process-boundary hop (node-side socket listener -> renderer-side services)
// makes every call async.

// Scope at which a setting is read or written.
// Mirrors VS Code ConfigurationTarget (platform/configuration/common/configuration.ts).
enum ConfigurationTarget {
CONFIGURATION_TARGET_UNSPECIFIED = 0;
CONFIGURATION_TARGET_APPLICATION = 1;
CONFIGURATION_TARGET_USER = 2;
CONFIGURATION_TARGET_USER_LOCAL = 3;
CONFIGURATION_TARGET_USER_REMOTE = 4;
CONFIGURATION_TARGET_WORKSPACE = 5;
CONFIGURATION_TARGET_WORKSPACE_FOLDER = 6;
}

// Marker severity. Bitmask values mirror VS Code MarkerSeverity
// (platform/markers/common/markers.ts): Hint=1, Info=2, Warning=4, Error=8.
enum MarkerSeverity {
MARKER_SEVERITY_UNSPECIFIED = 0;
MARKER_SEVERITY_HINT = 1;
MARKER_SEVERITY_INFO = 2;
MARKER_SEVERITY_WARNING = 4;
MARKER_SEVERITY_ERROR = 8;
}

// A position in a text document (1-based, matching VS Code IRange).
message Position {
int32 line = 1;
int32 column = 2;
}

// A range in a text document.
message Range {
Position start = 1;
Position end = 2;
}

// Outcome of a permission gate (IDialogService confirm/prompt + workspace trust).
enum PermissionDecision {
PERMISSION_DECISION_UNSPECIFIED = 0;
PERMISSION_DECISION_ALLOW_ONCE = 1;
PERMISSION_DECISION_ALLOW_ALWAYS = 2;
PERMISSION_DECISION_DENY = 3;
}
45 changes: 45 additions & 0 deletions agent/proto/ide/config/v1/config.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
syntax = "proto3";

package ide.config.v1;

import "google/protobuf/struct.proto";
import "ide/common/v1/common.proto";

// Settings read/write. Backed by IConfigurationService
// (platform/configuration/common/configuration.ts).
//
// Unlike the extension API (which rejects unregistered keys and cannot write
// non-persisted values, microsoft/vscode#43226), core updateValue writes any
// key to settings.json at the chosen scope. NOTE: writes are dropped during
// shutdown (microsoft/vscode#45474) — never issue from dispose().
service ConfigService {
// inspect(): returns the value at each scope (user/workspace/folder/default)
// so the agent knows where a key currently lives before writing.
rpc Inspect(InspectRequest) returns (InspectResponse);

// updateValue(): write at a scope. Omit value to remove the key.
rpc UpdateValue(UpdateValueRequest) returns (UpdateValueResponse);
}

message InspectRequest {
string key = 1;
// Optional folder URI for folder-scope inspection (IConfigurationOverrides.resource).
string resource_uri = 2;
}
message InspectResponse {
// google.protobuf.Value carries arbitrary JSON-shaped setting values.
google.protobuf.Value default_value = 1;
google.protobuf.Value user_value = 2;
google.protobuf.Value workspace_value = 3;
google.protobuf.Value workspace_folder_value = 4;
google.protobuf.Value effective_value = 5; // the resolved value
}

message UpdateValueRequest {
string key = 1;
// Omit (null) to remove the key from the target scope.
google.protobuf.Value value = 2;
ide.common.v1.ConfigurationTarget target = 3;
string resource_uri = 4; // required for WORKSPACE_FOLDER target
}
message UpdateValueResponse {}
Loading