@agentshouse/kit 0.1.0-alpha.16 → 0.1.0-alpha.17
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 +12 -10
- package/bootstrap.json +3 -3
- package/package.json +1 -1
- package/src/adapter-mcp.js +0 -13
- package/src/contract.js +5 -1
- package/src/house.js +48 -49
- package/src/inbound.js +2 -18
- package/src/launcher.js +2 -3
- package/src/worker.js +17 -9
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.17/connect-linux.sh)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The release asset pins one immutable multi-architecture image digest. It creates
|
|
@@ -760,11 +760,13 @@ prompt's stop reason through the contract's own table to one of four turn
|
|
|
760
760
|
outcomes: completed, interrupted, refused by the provider, or exhausted. A stop
|
|
761
761
|
reason the contract does not name, including a vendor-specific one a different
|
|
762
762
|
contract names, fails the turn as `kit_adapter_contract_mismatch` rather than
|
|
763
|
-
being guessed.
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
763
|
+
being guessed. After committing the Run, and again on every resumption, the
|
|
764
|
+
Kit reads what the Session was started for from House's
|
|
765
|
+
`/kit/runs/<run>/request` with the Session credential: the Routine Request with
|
|
766
|
+
its input, references, requester and the pinned revision's instruction, or the
|
|
767
|
+
sealed conversation turn. It sends that as the first prompt block, followed by
|
|
768
|
+
current House guidance fetched through the Run connection, so the Agent calls
|
|
769
|
+
nothing to learn it. An unreadable request or guidance refuses the turn. It forwards only adapter-classified User-visible assistant
|
|
768
770
|
text back over the local channel, and leaves reasoning, tool traces, and
|
|
769
771
|
terminal output where they are. The supervised process's own House calls travel
|
|
770
772
|
that same channel through the `house` CLI. The Kit holds the WorkerRunToken, replaces it in place when
|
|
@@ -910,8 +912,8 @@ receiving a complete byte stream, validating any reported size, and writing the
|
|
|
910
912
|
file. A lost acknowledgement preserves the completed local file for House's
|
|
911
913
|
idempotent admission and later cleanup. Failed files remain pending; House's
|
|
912
914
|
all-files-ready gate keeps their turn unprocessed.
|
|
913
|
-
The provider receives absolute local paths beside the owning messages
|
|
914
|
-
|
|
915
|
+
The provider receives absolute local paths beside the owning messages in its
|
|
916
|
+
first prompt. The cache persists across
|
|
915
917
|
turns; only House-authorized terminal Run cleanup removes its Run directory.
|
|
916
918
|
|
|
917
919
|
The accepted attachment's retained original is separate from that cache. The Kit
|
|
@@ -936,8 +938,8 @@ outcome it handled, and exits non-zero while any of them is unsaved.
|
|
|
936
938
|
One `kit worker run` then takes one House-authorized resumption, or otherwise
|
|
937
939
|
acquires one waiting Run, and supervises exactly that WorkerSession. A resumed
|
|
938
940
|
conversation loads its retained native provider session through ACP
|
|
939
|
-
`session/load`, receives the current input
|
|
940
|
-
|
|
941
|
+
`session/load`, receives the current input House answers for the Run in its
|
|
942
|
+
first prompt, and executes one turn. Historical output replayed during
|
|
941
943
|
loading is not emitted as new assistant output. Missing or rejected provider
|
|
942
944
|
continuation never starts a replacement provider conversation in the same Run.
|
|
943
945
|
|
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.17",
|
|
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.17/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.17/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|
package/package.json
CHANGED
package/src/adapter-mcp.js
CHANGED
|
@@ -29,16 +29,3 @@ export async function commandHouseInstructions(bridge) {
|
|
|
29
29
|
}
|
|
30
30
|
return frame.result.instructions;
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
export async function commandHouseRequest(bridge) {
|
|
34
|
-
const frame = await commandHouseReply(bridge, { method: "tools/call", params: { name: "session_get_request", arguments: {} } });
|
|
35
|
-
if (frame.error !== undefined || frame.result?.isError === true) throw new KitRefusal("kit_run_input_unreadable");
|
|
36
|
-
try {
|
|
37
|
-
const content = frame.result.content;
|
|
38
|
-
const input = JSON.parse(content.find((item) => item.type === "text").text);
|
|
39
|
-
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error();
|
|
40
|
-
return input;
|
|
41
|
-
} catch {
|
|
42
|
-
throw new KitRefusal("kit_run_input_unreadable");
|
|
43
|
-
}
|
|
44
|
-
}
|
package/src/contract.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CLAUDE_PASTED_CODE, CODEX_DEVICE_CODE, GROK_DEVICE_CODE } from "./agent-logins.js";
|
|
2
2
|
|
|
3
|
-
export const KIT_CONTRACT_VERSION = "2026-09-28-
|
|
3
|
+
export const KIT_CONTRACT_VERSION = "2026-09-28-first-prompt";
|
|
4
4
|
export const KIT_CAPTURE_BATCH_MAX = 100;
|
|
5
5
|
export const COMMAND_INPUT_MAX_BYTES = 4 * 1024;
|
|
6
6
|
export const AUTHORIZATION_OUTPUT_MAX_BYTES = 4 * 1024;
|
|
@@ -40,6 +40,10 @@ export function runOutcomePath(run) {
|
|
|
40
40
|
return `/kit/runs/${encodeURIComponent(run)}/outcome`;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export function runRequestPath(run) {
|
|
44
|
+
return `/kit/runs/${encodeURIComponent(run)}/request`;
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
export const KIT_SESSIONS_REPORT_PATH = "/kit/sessions/report";
|
|
44
48
|
export const KIT_SESSIONS_REPORT_MAX = 256;
|
|
45
49
|
|
package/src/house.js
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
MCP_PROTOCOL_VERSION,
|
|
30
30
|
WORKER_RUN_TOKEN_MARGIN_MS,
|
|
31
31
|
runOutcomePath,
|
|
32
|
+
runRequestPath,
|
|
32
33
|
secretInputPath,
|
|
33
34
|
SECRET_VALUE_MAX,
|
|
34
35
|
sessionInteractionsPath,
|
|
@@ -734,11 +735,7 @@ export async function reportLogin(session, login, outcome, signal = session.sign
|
|
|
734
735
|
}
|
|
735
736
|
|
|
736
737
|
const envelope = declarationReader("kit_acquire_rejected");
|
|
737
|
-
|
|
738
|
-
function routineInput(value, declaration) {
|
|
739
|
-
if (typeof value !== "string" || Array.from(value).length > 20_000) envelope.refuse(declaration);
|
|
740
|
-
return value;
|
|
741
|
-
}
|
|
738
|
+
const input = declarationReader("kit_run_input_unreadable");
|
|
742
739
|
|
|
743
740
|
function acquiredRoute(value, declaration) {
|
|
744
741
|
const route = envelope.plain(value, declaration);
|
|
@@ -756,79 +753,82 @@ function acquiredRoute(value, declaration) {
|
|
|
756
753
|
|
|
757
754
|
function conversationText(value, declaration) {
|
|
758
755
|
if (value === null) return null;
|
|
759
|
-
if (typeof value !== "string" || value.length > 4096)
|
|
756
|
+
if (typeof value !== "string" || value.length > 4096) input.refuse(declaration);
|
|
760
757
|
return value;
|
|
761
758
|
}
|
|
762
759
|
|
|
763
760
|
function conversationMessage(value, declaration) {
|
|
764
|
-
const message =
|
|
761
|
+
const message = input.plain(value, declaration);
|
|
765
762
|
const text = conversationText(message.text, `${declaration}.text`);
|
|
766
763
|
const caption = conversationText(message.caption, `${declaration}.caption`);
|
|
767
|
-
if (!Array.isArray(message.files))
|
|
764
|
+
if (!Array.isArray(message.files)) input.refuse(`${declaration}.files`);
|
|
768
765
|
const files = message.files.map((value, index) => {
|
|
769
766
|
const name = `${declaration}.files[${index}]`;
|
|
770
|
-
const file =
|
|
767
|
+
const file = input.plain(value, name);
|
|
771
768
|
if (file.bytes !== null && (!Number.isSafeInteger(file.bytes) || file.bytes < 0)) {
|
|
772
|
-
|
|
769
|
+
input.refuse(`${name}.bytes`);
|
|
773
770
|
}
|
|
774
771
|
return {
|
|
775
|
-
path:
|
|
776
|
-
media_kind:
|
|
777
|
-
media_type: file.media_type === null ? null :
|
|
772
|
+
path: input.text(file.path, `${name}.path`, 2048),
|
|
773
|
+
media_kind: input.text(file.media_kind, `${name}.media_kind`, 128),
|
|
774
|
+
media_type: file.media_type === null ? null : input.text(file.media_type, `${name}.media_type`, 255),
|
|
778
775
|
bytes: file.bytes,
|
|
779
776
|
};
|
|
780
777
|
});
|
|
781
|
-
if (!text && !caption && files.length === 0)
|
|
778
|
+
if (!text && !caption && files.length === 0) input.refuse(declaration);
|
|
782
779
|
return {
|
|
783
|
-
message_id:
|
|
780
|
+
message_id: input.text(message.message_id, `${declaration}.message_id`, 128),
|
|
784
781
|
text, caption, files,
|
|
785
782
|
};
|
|
786
783
|
}
|
|
787
784
|
|
|
788
|
-
function
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
const turn =
|
|
785
|
+
function runRequest(answer) {
|
|
786
|
+
const request = input.plain(answer, "request");
|
|
787
|
+
if (request.kind === "conversation") {
|
|
788
|
+
if (request.origin !== "user") input.refuse("request.origin");
|
|
789
|
+
const turn = input.plain(request.turn, "request.turn");
|
|
793
790
|
return {
|
|
791
|
+
kind: "conversation",
|
|
794
792
|
origin: "user",
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
.map((message, index) => conversationMessage(message, `${declaration}.conversation.turn.messages[${index}]`)),
|
|
801
|
-
},
|
|
793
|
+
turn: {
|
|
794
|
+
id: input.text(turn.id, "request.turn.id", 128),
|
|
795
|
+
sequence: input.positiveCount(turn.sequence, "request.turn.sequence"),
|
|
796
|
+
messages: input.filled(turn.messages, "request.turn.messages")
|
|
797
|
+
.map((message, index) => conversationMessage(message, `request.turn.messages[${index}]`)),
|
|
802
798
|
},
|
|
803
799
|
};
|
|
804
800
|
}
|
|
805
|
-
if (
|
|
806
|
-
const
|
|
807
|
-
|
|
801
|
+
if (request.kind !== "routine") input.refuse("request.kind");
|
|
802
|
+
const routine = input.plain(request.routine, "request.routine");
|
|
803
|
+
if (typeof request.payload !== "string" || Array.from(request.payload).length > 20_000) input.refuse("request.payload");
|
|
804
|
+
if (!Array.isArray(request.references)) input.refuse("request.references");
|
|
808
805
|
return {
|
|
809
|
-
|
|
806
|
+
kind: "routine",
|
|
807
|
+
origin: input.text(request.origin, "request.origin", 32),
|
|
810
808
|
routine: {
|
|
811
|
-
name:
|
|
812
|
-
version:
|
|
809
|
+
name: input.text(routine.name, "request.routine.name", 256),
|
|
810
|
+
version: input.positiveCount(routine.version, "request.routine.version"),
|
|
813
811
|
},
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
),
|
|
820
|
-
requested_at: envelope.text(
|
|
821
|
-
request.requested_at,
|
|
822
|
-
`${declaration}.request.requested_at`,
|
|
823
|
-
64,
|
|
824
|
-
),
|
|
825
|
-
deliver_at:
|
|
826
|
-
request.deliver_at === null
|
|
827
|
-
? null
|
|
828
|
-
: envelope.text(request.deliver_at, `${declaration}.request.deliver_at`, 64),
|
|
812
|
+
instruction: input.text(request.instruction, "request.instruction", 256 * 1024),
|
|
813
|
+
payload: request.payload,
|
|
814
|
+
references: request.references.map((reference, index) => input.plain(reference, `request.references[${index}]`)),
|
|
815
|
+
requested_by: input.text(request.requested_by, "request.requested_by", 128),
|
|
816
|
+
requested_at: input.text(request.requested_at, "request.requested_at", 64),
|
|
817
|
+
deliver_at: request.deliver_at === null ? null : input.text(request.deliver_at, "request.deliver_at", 64),
|
|
829
818
|
};
|
|
830
819
|
}
|
|
831
820
|
|
|
821
|
+
export async function readRunRequest(session, run, token, signal = session.signal) {
|
|
822
|
+
return runRequest(await houseRequest(
|
|
823
|
+
session.house,
|
|
824
|
+
`Bearer ${token}`,
|
|
825
|
+
runRequestPath(run),
|
|
826
|
+
{},
|
|
827
|
+
session.dependencies,
|
|
828
|
+
signal,
|
|
829
|
+
));
|
|
830
|
+
}
|
|
831
|
+
|
|
832
832
|
function acquiredAssignment(value, declaration) {
|
|
833
833
|
const acquired = envelope.plain(value, declaration);
|
|
834
834
|
const bridge = envelope.plain(acquired.session, `${declaration}.session`);
|
|
@@ -857,7 +857,6 @@ function acquiredAssignment(value, declaration) {
|
|
|
857
857
|
token: envelope.text(bridge.bridge_token, `${declaration}.session.bridge_token`, 512),
|
|
858
858
|
expiresAt: Date.parse(expiresAt),
|
|
859
859
|
},
|
|
860
|
-
request: acquiredRequest(acquired, declaration),
|
|
861
860
|
};
|
|
862
861
|
}
|
|
863
862
|
|
package/src/inbound.js
CHANGED
|
@@ -134,13 +134,9 @@ export async function reconcileInbound(directory, session) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
export async function localRequest(directory, run, request) {
|
|
137
|
-
|
|
138
|
-
if (turn === null || turn === undefined) return request;
|
|
137
|
+
if (request.kind !== "conversation") return request;
|
|
139
138
|
const result = structuredClone(request);
|
|
140
|
-
const
|
|
141
|
-
if (!Array.isArray(localTurn.messages)) throw new KitRefusal("kit_run_input_unreadable");
|
|
142
|
-
for (const message of localTurn.messages) {
|
|
143
|
-
if (!Array.isArray(message.files)) throw new KitRefusal("kit_run_input_unreadable");
|
|
139
|
+
for (const message of result.turn.messages) {
|
|
144
140
|
for (const file of message.files) {
|
|
145
141
|
const parts = typeof file.path === "string" ? file.path.split("/") : [];
|
|
146
142
|
if (parts.length !== 4 || parts[0] !== ROOT || parts[1] !== run || !UUID.test(parts[1]) || !UUID.test(parts[2]) || !segment(parts[3])) {
|
|
@@ -161,15 +157,3 @@ export async function localRequest(directory, run, request) {
|
|
|
161
157
|
}
|
|
162
158
|
return result;
|
|
163
159
|
}
|
|
164
|
-
|
|
165
|
-
export async function localReply(directory, run, frame, reply) {
|
|
166
|
-
if (frame.method !== "tools/call" || frame.params?.name !== "session_get_request" || reply.error || reply.result?.isError) return reply;
|
|
167
|
-
const result = structuredClone(reply);
|
|
168
|
-
for (const item of result.result?.content ?? []) {
|
|
169
|
-
if (item.type !== "text") continue;
|
|
170
|
-
let input;
|
|
171
|
-
try { input = JSON.parse(item.text); } catch { throw new KitRefusal("kit_run_input_unreadable"); }
|
|
172
|
-
item.text = JSON.stringify(await localRequest(directory, run, input));
|
|
173
|
-
}
|
|
174
|
-
return result;
|
|
175
|
-
}
|
package/src/launcher.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { commandHouseInstructions
|
|
2
|
+
import { commandHouseInstructions } from "./adapter-mcp.js";
|
|
3
3
|
import {
|
|
4
4
|
compiledContract,
|
|
5
5
|
initializeCommandAdapter,
|
|
@@ -191,13 +191,12 @@ async function runTurn(channel, start, bridge, turn) {
|
|
|
191
191
|
loading = false;
|
|
192
192
|
}
|
|
193
193
|
await configureSession(acp, contract, providerSessionId, start.adapter);
|
|
194
|
-
const request = start.request === null ? await commandHouseRequest(bridge) : start.request;
|
|
195
194
|
turn.adapterSession = true;
|
|
196
195
|
turn.cancel = () => acp.cancel({ sessionId: providerSessionId });
|
|
197
196
|
const prompted = await acp.request("session/prompt", {
|
|
198
197
|
sessionId: providerSessionId,
|
|
199
198
|
prompt: [
|
|
200
|
-
{ type: "text", text: JSON.stringify(request) },
|
|
199
|
+
{ type: "text", text: JSON.stringify(start.request) },
|
|
201
200
|
{ type: "text", text: instructions },
|
|
202
201
|
...workingCopyContext(start.working_copies),
|
|
203
202
|
],
|
package/src/worker.js
CHANGED
|
@@ -14,7 +14,7 @@ import { openBridge } from "./bridge.js";
|
|
|
14
14
|
import { assistantBlocks } from "./blocks.js";
|
|
15
15
|
import { retryConversationFiles } from "./conversation-originals.js";
|
|
16
16
|
import { openAssistantDrafts } from "./draft.js";
|
|
17
|
-
import {
|
|
17
|
+
import { localRequest, reconcileInbound } from "./inbound.js";
|
|
18
18
|
import { localFiles } from "./local-files.js";
|
|
19
19
|
import { inheritedEnvironment } from "./command.js";
|
|
20
20
|
import { watchKitWork } from "./kit-work-stream.js";
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
observeSessionStart,
|
|
53
53
|
observeSessionStop,
|
|
54
54
|
openSession,
|
|
55
|
+
readRunRequest,
|
|
55
56
|
replaceSessionAuthority,
|
|
56
57
|
reportContinuation,
|
|
57
58
|
reportInterruptOutcome,
|
|
@@ -292,12 +293,12 @@ async function openRunBridge(context, bound, authority, start) {
|
|
|
292
293
|
settle: handed((interaction, outcome) => settleInteraction(context, interaction, outcome)),
|
|
293
294
|
forward: handed((frame, signal) =>
|
|
294
295
|
localFiles(async (sent) =>
|
|
295
|
-
|
|
296
|
+
callHouseMcp(
|
|
296
297
|
context.session,
|
|
297
298
|
await sessionAuthority(context.session, bound.session, authority, signal),
|
|
298
299
|
sent,
|
|
299
300
|
signal,
|
|
300
|
-
)
|
|
301
|
+
), context.session.dependencies, signal)(frame)),
|
|
301
302
|
});
|
|
302
303
|
return { bridge, drafts, ended };
|
|
303
304
|
}
|
|
@@ -467,7 +468,7 @@ async function prepareAssignment(context, assignment, resumed) {
|
|
|
467
468
|
reported: null,
|
|
468
469
|
};
|
|
469
470
|
const authority = { token: assignment.session.token, expiresAt: assignment.session.expiresAt };
|
|
470
|
-
const
|
|
471
|
+
const start = {
|
|
471
472
|
working_directory: assignment.workingDirectory,
|
|
472
473
|
working_copies: await appliedWorkingCopies(context),
|
|
473
474
|
execution_authority: context.configuration.executionAuthority,
|
|
@@ -479,10 +480,16 @@ async function prepareAssignment(context, assignment, resumed) {
|
|
|
479
480
|
effort: route.effort,
|
|
480
481
|
environment: {},
|
|
481
482
|
},
|
|
482
|
-
request:
|
|
483
|
+
request: null,
|
|
483
484
|
provider_session: assignment.providerSessionId ?? null,
|
|
484
|
-
}
|
|
485
|
-
|
|
485
|
+
};
|
|
486
|
+
const opened = await openRunBridge(context, bound, authority, start);
|
|
487
|
+
const request = async () => {
|
|
488
|
+
const token = await sessionAuthority(context.session, bound.session, authority);
|
|
489
|
+
start.request = await localRequest(context.directory, bound.run,
|
|
490
|
+
await readRunRequest(context.session, bound.run, token));
|
|
491
|
+
};
|
|
492
|
+
return { bound, opened, request };
|
|
486
493
|
}
|
|
487
494
|
|
|
488
495
|
async function refuseContinuation(context, command, failure) {
|
|
@@ -523,11 +530,12 @@ async function superviseAssignment(context, assignment, resumed = false) {
|
|
|
523
530
|
if (undecided(context, refusal)) throw refusal;
|
|
524
531
|
return refuseAssignment(context, assignment, failure);
|
|
525
532
|
}
|
|
526
|
-
const { bound, opened } = prepared;
|
|
533
|
+
const { bound, opened, request } = prepared;
|
|
527
534
|
return superviseBound(context, bound, opened, async (spawn) => {
|
|
528
535
|
if (!resumed) await commitRun(context.session, assignment.run);
|
|
529
536
|
spawn.committed = true;
|
|
530
537
|
await writeBinding(context.directory, bound);
|
|
538
|
+
await request();
|
|
531
539
|
await createSession(context.runtime, {
|
|
532
540
|
session: bound.tmux,
|
|
533
541
|
workingDirectory: assignment.workingDirectory,
|
|
@@ -549,7 +557,7 @@ async function superviseResumption(context, command) {
|
|
|
549
557
|
}
|
|
550
558
|
return superviseAssignment(context, {
|
|
551
559
|
...command, agentId: held.agentId, workingDirectory: held.workingDirectory,
|
|
552
|
-
conversation: held.conversation, providerSessionId: held.providerSessionId,
|
|
560
|
+
conversation: held.conversation, providerSessionId: held.providerSessionId,
|
|
553
561
|
}, true);
|
|
554
562
|
}
|
|
555
563
|
|