@agentshouse/kit 0.1.0-alpha.23 → 0.1.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/bootstrap.json +3 -3
- package/package.json +1 -1
- package/src/adapter-mcp.js +57 -2
- package/src/contract.js +4 -0
- package/src/house.js +4 -0
- package/src/launcher.js +10 -2
- package/src/worker.js +1 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ On Linux `amd64` or `arm64` with a working Docker Engine, run the published,
|
|
|
10
10
|
versioned bootstrap once:
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
13
|
+
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.24/connect-linux.sh)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The release asset pins one immutable multi-architecture image digest. It creates
|
|
@@ -781,7 +781,18 @@ Kit reads what the Session was started for from House's
|
|
|
781
781
|
its input, references, requester and the pinned revision's instruction, or the
|
|
782
782
|
sealed conversation turn. It sends that as the first prompt block, followed by
|
|
783
783
|
current House guidance fetched through the Run connection, so the Agent calls
|
|
784
|
-
nothing to learn it. An unreadable request or guidance refuses the turn.
|
|
784
|
+
nothing to learn it. An unreadable request or guidance refuses the turn.
|
|
785
|
+
A session opened with `session/new` gets one more text block ahead of those,
|
|
786
|
+
the same for every adapter kind: the line
|
|
787
|
+
`The agents.house house CLI is available: run house --help.`, the base
|
|
788
|
+
instructions of the Execution attempt plan House acquired, and the current text
|
|
789
|
+
of the owner's How-we-work document `/private/library/how-we-work.md`, which the
|
|
790
|
+
launcher reads with the `shell` tool over the Session's own bridge, following
|
|
791
|
+
House's cursor to the whole text. An absent document, one the Session's ceiling
|
|
792
|
+
cannot read, and empty base instructions add nothing. Any other failed read
|
|
793
|
+
refuses the attempt as `kit_how_we_work_unreadable` with the bridge's cause
|
|
794
|
+
before a prompt is sent. A session reloaded with `session/load` already holds
|
|
795
|
+
the block and gets none. It forwards only adapter-classified User-visible assistant
|
|
785
796
|
text back over the local channel, and leaves reasoning, tool traces, and
|
|
786
797
|
terminal output where they are. The supervised process's own House calls travel
|
|
787
798
|
that same channel through the `house` CLI. The Kit holds the WorkerRunToken, replaces it in place when
|
package/bootstrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.24",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"linux": {
|
|
6
|
-
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
6
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.24/connect-linux.sh)",
|
|
7
7
|
"asset": {
|
|
8
8
|
"name": "connect-linux.sh",
|
|
9
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
9
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.24/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|
package/package.json
CHANGED
package/src/adapter-mcp.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
COMMAND_ADAPTER_INITIALIZE_TIMEOUT_MS,
|
|
3
|
+
HOW_WE_WORK_PATH,
|
|
4
|
+
MCP_PROTOCOL_VERSION,
|
|
5
|
+
SHELL_RENDER,
|
|
6
|
+
} from "./contract.js";
|
|
7
|
+
import { answerText, bridgeCall } from "./mcp.js";
|
|
3
8
|
import { KitRefusal } from "./refusal.js";
|
|
4
9
|
|
|
5
10
|
function readableDiscovery(discovery) {
|
|
@@ -29,3 +34,53 @@ export async function commandHouseInstructions(bridge) {
|
|
|
29
34
|
}
|
|
30
35
|
return frame.result.instructions;
|
|
31
36
|
}
|
|
37
|
+
|
|
38
|
+
function shellAnswer(text) {
|
|
39
|
+
const lines = text.split("\n");
|
|
40
|
+
if (!lines[0].startsWith(`protocol: render=${SHELL_RENDER} `)) return null;
|
|
41
|
+
const exit = Number(lines[2].slice("exit: ".length));
|
|
42
|
+
let at = lines[5]?.startsWith("authority: ") ? 6 : 5;
|
|
43
|
+
if (exit === 0) return { exit, stderr: [], stdout: lines.slice(at).join("\n"), cursor: null };
|
|
44
|
+
const stderr = [];
|
|
45
|
+
while (lines[at]?.startsWith("stderr: ")) stderr.push(lines[at++].slice("stderr: ".length));
|
|
46
|
+
const stdout = lines.slice(at);
|
|
47
|
+
const cursor = stdout.at(-1)?.startsWith("cursor: ") ? stdout.pop().slice("cursor: ".length) : null;
|
|
48
|
+
return { exit, stderr, stdout: stdout.join("\n"), cursor };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function howWeWorkPage(bridge, cursor) {
|
|
52
|
+
const frame = await bridgeCall(bridge, {
|
|
53
|
+
method: "tools/call",
|
|
54
|
+
params: {
|
|
55
|
+
name: "shell",
|
|
56
|
+
arguments: { command: `cat ${HOW_WE_WORK_PATH}`, ...(cursor === null ? {} : { cursor }) },
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
if (frame.error !== undefined) {
|
|
60
|
+
throw new KitRefusal("kit_how_we_work_unreadable", frame.error.message ?? null);
|
|
61
|
+
}
|
|
62
|
+
const text = answerText(frame.result);
|
|
63
|
+
if (frame.result?.isError === true) {
|
|
64
|
+
if (text.split(":")[0] === "room_not_found") return null;
|
|
65
|
+
throw new KitRefusal("kit_how_we_work_unreadable", text);
|
|
66
|
+
}
|
|
67
|
+
const page = shellAnswer(text);
|
|
68
|
+
if (page === null) throw new KitRefusal("kit_how_we_work_unreadable", text);
|
|
69
|
+
if (page.cursor === null && page.exit !== 0) {
|
|
70
|
+
if (page.stderr.length === 1 && page.stderr[0].split(" ")[1] === "path_not_found") return null;
|
|
71
|
+
throw new KitRefusal("kit_how_we_work_unreadable", page.stderr.join("\n"));
|
|
72
|
+
}
|
|
73
|
+
return page;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function commandHouseHowWeWork(bridge) {
|
|
77
|
+
const pages = [];
|
|
78
|
+
let cursor = null;
|
|
79
|
+
do {
|
|
80
|
+
const page = await howWeWorkPage(bridge, cursor);
|
|
81
|
+
if (page === null) return "";
|
|
82
|
+
pages.push(page.stdout);
|
|
83
|
+
cursor = page.cursor;
|
|
84
|
+
} while (cursor !== null);
|
|
85
|
+
return pages.join("\n");
|
|
86
|
+
}
|
package/src/contract.js
CHANGED
|
@@ -118,6 +118,10 @@ export const BRIDGE_NONCE_ENVIRONMENT = "HOUSE_KIT_BRIDGE_NONCE";
|
|
|
118
118
|
export const BRIDGE_RECONNECT_MS = 200;
|
|
119
119
|
export const BRIDGE_RECONNECT_MAX_MS = 5_000;
|
|
120
120
|
|
|
121
|
+
export const HOUSE_CLI_LINE = "The agents.house house CLI is available: run house --help.";
|
|
122
|
+
export const HOW_WE_WORK_PATH = "/private/library/how-we-work.md";
|
|
123
|
+
export const SHELL_RENDER = "shell/1";
|
|
124
|
+
|
|
121
125
|
export const ACP_PROTOCOL = 1;
|
|
122
126
|
export const ACP_AUTH_REQUIRED = -32000;
|
|
123
127
|
export const ADAPTER_EXIT_GRACE_MS = 2_000;
|
package/src/house.js
CHANGED
|
@@ -855,9 +855,13 @@ function acquiredAssignment(value, declaration) {
|
|
|
855
855
|
if (Number.isNaN(Date.parse(expiresAt))) {
|
|
856
856
|
envelope.refuse(`${declaration}.session.bridge_token_expires_at`);
|
|
857
857
|
}
|
|
858
|
+
if (typeof plan.base_instructions !== "string") {
|
|
859
|
+
envelope.refuse(`${declaration}.plan.base_instructions`);
|
|
860
|
+
}
|
|
858
861
|
return {
|
|
859
862
|
run: envelope.text(acquired.run, `${declaration}.run`, 128),
|
|
860
863
|
agentId: envelope.text(plan.agent_id, `${declaration}.plan.agent_id`, 128),
|
|
864
|
+
baseInstructions: plan.base_instructions,
|
|
861
865
|
workingDirectory: envelope.text(
|
|
862
866
|
plan.working_directory,
|
|
863
867
|
`${declaration}.plan.working_directory`,
|
package/src/launcher.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { commandHouseInstructions } from "./adapter-mcp.js";
|
|
2
|
+
import { commandHouseHowWeWork, commandHouseInstructions } from "./adapter-mcp.js";
|
|
3
3
|
import {
|
|
4
4
|
compiledContract,
|
|
5
5
|
initializeCommandAdapter,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
startCommandAdapter,
|
|
8
8
|
stopCommandAdapter,
|
|
9
9
|
} from "./adapter.js";
|
|
10
|
-
import { PERMISSION_OPTION_KINDS, SESSION_SET_CONFIG_OPTION } from "./contract.js";
|
|
10
|
+
import { HOUSE_CLI_LINE, PERMISSION_OPTION_KINDS, SESSION_SET_CONFIG_OPTION } from "./contract.js";
|
|
11
11
|
import { observedAssistantDrafts } from "./draft.js";
|
|
12
12
|
import { observedPlans } from "./plan.js";
|
|
13
13
|
import { bridgePeer } from "./peer.js";
|
|
@@ -129,6 +129,11 @@ async function configureSession(acp, contract, sessionId, adapter) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
async function sessionOpening(bridge, baseInstructions) {
|
|
133
|
+
const parts = [HOUSE_CLI_LINE, baseInstructions, await commandHouseHowWeWork(bridge)];
|
|
134
|
+
return [{ type: "text", text: parts.filter((part) => part.trim() !== "").join("\n\n") }];
|
|
135
|
+
}
|
|
136
|
+
|
|
132
137
|
function workingCopyContext(copies) {
|
|
133
138
|
if (!Array.isArray(copies) || copies.length === 0) return [];
|
|
134
139
|
const lines = copies.map((copy) => `${copy.path}: ${copy.repository}`);
|
|
@@ -179,6 +184,8 @@ async function runTurn(channel, start, bridge, turn) {
|
|
|
179
184
|
const { acp } = await initializeCommandAdapter(adapter, contract, handlers);
|
|
180
185
|
const instructions = await commandHouseInstructions(bridge);
|
|
181
186
|
let providerSessionId = start.provider_session ?? null;
|
|
187
|
+
const opening =
|
|
188
|
+
providerSessionId === null ? await sessionOpening(bridge, start.base_instructions) : [];
|
|
182
189
|
const setup = { cwd: start.working_directory, mcpServers: [] };
|
|
183
190
|
if (providerSessionId === null) {
|
|
184
191
|
providerSessionId = openedSession(await acp.request("session/new", setup));
|
|
@@ -196,6 +203,7 @@ async function runTurn(channel, start, bridge, turn) {
|
|
|
196
203
|
const prompted = await acp.request("session/prompt", {
|
|
197
204
|
sessionId: providerSessionId,
|
|
198
205
|
prompt: [
|
|
206
|
+
...opening,
|
|
199
207
|
{ type: "text", text: JSON.stringify(start.request) },
|
|
200
208
|
{ type: "text", text: instructions },
|
|
201
209
|
...workingCopyContext(start.working_copies),
|
package/src/worker.js
CHANGED
|
@@ -575,6 +575,7 @@ async function prepareAssignment(context, assignment, resumed) {
|
|
|
575
575
|
};
|
|
576
576
|
const authority = { token: assignment.session.token, expiresAt: assignment.session.expiresAt };
|
|
577
577
|
const start = {
|
|
578
|
+
base_instructions: assignment.baseInstructions,
|
|
578
579
|
working_directory: assignment.workingDirectory,
|
|
579
580
|
working_copies: await appliedWorkingCopies(context),
|
|
580
581
|
execution_authority: context.configuration.executionAuthority,
|