@agentshouse/kit 0.2.1-cb6679d048e60 → 0.2.1-ce145e9aadbf6
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/dist/agents.js +38 -19
- package/dist/api.js +2 -1
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +5 -3
- package/dist/clis.js +19 -4
- package/dist/conversations.js +5 -2
- package/dist/copies.js +26 -31
- package/dist/files.js +2 -1
- package/dist/instructions.js +1 -1
- package/dist/mcp.js +2 -1
- package/dist/relay.js +10 -0
- package/dist/secret-input.js +3 -1
- package/dist/sign-in.js +2 -1
- package/dist/skills.js +1 -1
- package/dist/stream.js +2 -1
- package/package.json +1 -1
package/dist/agents.js
CHANGED
|
@@ -3,13 +3,16 @@ import { readFile, realpath } from 'node:fs/promises';
|
|
|
3
3
|
import { killTree, startAdapter } from './acp.js';
|
|
4
4
|
import { CLIS, KIT_VERSION, below, installAdapter, installCli, locate, run, versionOf } from './clis.js';
|
|
5
5
|
import { agentBase, kitHome } from './home.js';
|
|
6
|
-
const SIGNED_OUT = { signed_in: false, models: [] };
|
|
6
|
+
const SIGNED_OUT = { signed_in: false, models: [], modes: [] };
|
|
7
7
|
function select(options, category) {
|
|
8
8
|
const option = options.find((candidate) => candidate.category === category);
|
|
9
9
|
return option?.type === 'select' ? option : undefined;
|
|
10
10
|
}
|
|
11
|
+
function choices(options) {
|
|
12
|
+
return options.flatMap((entry) => ('group' in entry ? entry.options : [entry]));
|
|
13
|
+
}
|
|
11
14
|
function values(options) {
|
|
12
|
-
return
|
|
15
|
+
return choices(options).map((entry) => entry.value);
|
|
13
16
|
}
|
|
14
17
|
function causeOf(error) {
|
|
15
18
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -21,9 +24,11 @@ async function models(kind, adapter, known) {
|
|
|
21
24
|
const agent = adapter.connection.agent;
|
|
22
25
|
const opened = await agent.request('session/new', { cwd: kitHome(), mcpServers: [] });
|
|
23
26
|
let options = opened.configOptions ?? [];
|
|
27
|
+
const mode = select(options, 'mode');
|
|
28
|
+
const modes = mode === undefined ? [] : choices(mode.options).map(({ value, name }) => ({ mode: value, name }));
|
|
24
29
|
const model = select(options, 'model');
|
|
25
30
|
if (model === undefined)
|
|
26
|
-
return [];
|
|
31
|
+
return { models: [], modes };
|
|
27
32
|
const offered = [];
|
|
28
33
|
for (const value of values(model.options)) {
|
|
29
34
|
if (select(options, 'model')?.currentValue !== value) {
|
|
@@ -39,7 +44,13 @@ async function models(kind, adapter, known) {
|
|
|
39
44
|
const effort = select(options, 'thought_level');
|
|
40
45
|
offered.push({ model: value, efforts: effort === undefined ? [] : values(effort.options) });
|
|
41
46
|
}
|
|
42
|
-
return offered;
|
|
47
|
+
return { models: offered, modes };
|
|
48
|
+
}
|
|
49
|
+
async function signedIn(kind, cli, known) {
|
|
50
|
+
const probe = CLIS[kind].signedIn;
|
|
51
|
+
if ('command' in probe)
|
|
52
|
+
return (await run(cli, probe.command, 30_000)).status === 0;
|
|
53
|
+
return (await offer(kind, cli, known)).signed_in;
|
|
43
54
|
}
|
|
44
55
|
async function offer(kind, cli, known) {
|
|
45
56
|
const probe = CLIS[kind].signedIn;
|
|
@@ -53,11 +64,15 @@ async function offer(kind, cli, known) {
|
|
|
53
64
|
if (typeof method !== 'string' || method.length === 0)
|
|
54
65
|
return SIGNED_OUT;
|
|
55
66
|
}
|
|
56
|
-
return { signed_in: true,
|
|
67
|
+
return { signed_in: true, ...(await models(kind, adapter, known.models)) };
|
|
57
68
|
}
|
|
58
69
|
catch (error) {
|
|
59
70
|
process.stderr.write(`kit: ${kind} did not offer its models: ${causeOf(error)}\n`);
|
|
60
|
-
return {
|
|
71
|
+
return {
|
|
72
|
+
signed_in: adapter === undefined && 'initializeMeta' in probe ? known.signed_in : true,
|
|
73
|
+
models: known.models,
|
|
74
|
+
modes: known.modes,
|
|
75
|
+
};
|
|
61
76
|
}
|
|
62
77
|
finally {
|
|
63
78
|
if (adapter !== undefined)
|
|
@@ -72,6 +87,8 @@ function reported(kind, reading) {
|
|
|
72
87
|
failure: reading.failure,
|
|
73
88
|
signed_in: reading.signed_in,
|
|
74
89
|
models: reading.models,
|
|
90
|
+
modes: reading.modes,
|
|
91
|
+
full_access: CLIS[kind].fullAccess,
|
|
75
92
|
};
|
|
76
93
|
}
|
|
77
94
|
function tooOld(kind, release) {
|
|
@@ -114,7 +131,7 @@ export class Agents {
|
|
|
114
131
|
});
|
|
115
132
|
this.reading = reading;
|
|
116
133
|
return reading.then(({ agents }) => {
|
|
117
|
-
Promise.all(agents.map((kind) => this.
|
|
134
|
+
Promise.all(agents.map((kind) => this.load(kind)))
|
|
118
135
|
.then(() => this.report())
|
|
119
136
|
.catch(logged);
|
|
120
137
|
});
|
|
@@ -130,7 +147,7 @@ export class Agents {
|
|
|
130
147
|
const known = this.readings.get(kind);
|
|
131
148
|
const kept = known?.release === null || known?.minimum !== undefined ? SIGNED_OUT : (known ?? SIGNED_OUT);
|
|
132
149
|
if (known?.release !== release || known.minimum !== undefined) {
|
|
133
|
-
this.update(kind, { release, failure: null, signed_in: kept.signed_in, models: kept.models });
|
|
150
|
+
this.update(kind, { release, failure: null, signed_in: kept.signed_in, models: kept.models, modes: kept.modes });
|
|
134
151
|
}
|
|
135
152
|
const read = this.nextRead(kind);
|
|
136
153
|
offer(kind, cli, kept)
|
|
@@ -147,10 +164,12 @@ export class Agents {
|
|
|
147
164
|
return this.queue(kind, () => this.ensure(kind));
|
|
148
165
|
}
|
|
149
166
|
async reread(kind) {
|
|
150
|
-
await this.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
await this.examine(kind, this.readings.get(kind) ?? SIGNED_OUT, offer);
|
|
168
|
+
await this.report();
|
|
169
|
+
}
|
|
170
|
+
async recheck(kind) {
|
|
171
|
+
const known = this.readings.get(kind) ?? SIGNED_OUT;
|
|
172
|
+
await this.examine(kind, known, async (kind, cli) => (await signedIn(kind, cli, known)) ? { ...known, signed_in: true } : SIGNED_OUT);
|
|
154
173
|
await this.report();
|
|
155
174
|
}
|
|
156
175
|
report() {
|
|
@@ -207,18 +226,18 @@ export class Agents {
|
|
|
207
226
|
return { release, minimum, failure: null, ...SIGNED_OUT };
|
|
208
227
|
return { cli, release };
|
|
209
228
|
}
|
|
210
|
-
async examine(kind, known) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
async examine(kind, known, read) {
|
|
230
|
+
const current = this.nextRead(kind);
|
|
231
|
+
const resolved = await this.queue(kind, () => this.resolve(kind));
|
|
232
|
+
const reading = 'cli' in resolved ? { release: resolved.release, failure: null, ...(await read(kind, resolved.cli, known)) } : resolved;
|
|
233
|
+
if (this.reads.get(kind) === current)
|
|
234
|
+
this.readings.set(kind, reading);
|
|
215
235
|
}
|
|
216
236
|
async load(kind) {
|
|
217
237
|
const known = this.readings.get(kind);
|
|
218
238
|
if (known !== undefined && known.failure === null)
|
|
219
239
|
return;
|
|
220
|
-
this.
|
|
221
|
-
this.readings.set(kind, await this.examine(kind, SIGNED_OUT));
|
|
240
|
+
await this.examine(kind, SIGNED_OUT, offer);
|
|
222
241
|
}
|
|
223
242
|
async send() {
|
|
224
243
|
const agents = this.desired.agents.flatMap((kind) => {
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readEnrolment } from './home.js';
|
|
2
|
+
import { relayed } from './relay.js';
|
|
2
3
|
export class HouseRefusal extends Error {
|
|
3
4
|
status;
|
|
4
5
|
text;
|
|
@@ -12,7 +13,7 @@ export function retryDelay(attempt) {
|
|
|
12
13
|
return Math.random() * Math.min(30_000, 1000 * 2 ** attempt);
|
|
13
14
|
}
|
|
14
15
|
export async function post(house, path, body, credential, signal) {
|
|
15
|
-
const answer = await fetch(new URL(path, house), {
|
|
16
|
+
const answer = await fetch(relayed(new URL(path, house)), {
|
|
16
17
|
method: 'POST',
|
|
17
18
|
headers: {
|
|
18
19
|
'content-type': 'application/json',
|
package/dist/bootstrap.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"version": "0.2.1-
|
|
3
|
+
"version": "0.2.1-ce145e9aadbf6",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"macos": {
|
|
6
6
|
"asset": {
|
|
7
7
|
"name": "connect-linux.sh",
|
|
8
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
8
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-ce145e9aadbf6/connect-linux.sh"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"linux": {
|
|
12
12
|
"asset": {
|
|
13
13
|
"name": "connect-linux.sh",
|
|
14
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
14
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-ce145e9aadbf6/connect-linux.sh"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"windows": {
|
|
18
18
|
"asset": {
|
|
19
19
|
"name": "connect-windows.ps1",
|
|
20
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
20
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-ce145e9aadbf6/connect-windows.ps1"
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
package/dist/bridge.js
CHANGED
|
@@ -13,6 +13,7 @@ import { kitHome, readEnrolment } from './home.js';
|
|
|
13
13
|
import { callHouse, mcpBody, rpc, UNREACHABLE } from './mcp.js';
|
|
14
14
|
import { operationId, uuidOf } from './operation.js';
|
|
15
15
|
import { refusalOf } from './refusals.js';
|
|
16
|
+
import { relayed } from './relay.js';
|
|
16
17
|
const MCP_REQUEST_BYTES = 4 * 1024 * 1024;
|
|
17
18
|
const ATTACHMENTS = '/kit/attachments/upload';
|
|
18
19
|
export async function received(request) {
|
|
@@ -22,7 +23,8 @@ export async function received(request) {
|
|
|
22
23
|
text += chunk;
|
|
23
24
|
return text;
|
|
24
25
|
}
|
|
25
|
-
function relay(request, response,
|
|
26
|
+
function relay(request, response, house, authorization) {
|
|
27
|
+
const target = relayed(house);
|
|
26
28
|
const send = target.protocol === 'https:' ? httpsRequest : httpRequest;
|
|
27
29
|
const forwarded = send(target, {
|
|
28
30
|
method: request.method,
|
|
@@ -117,7 +119,7 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
117
119
|
if ('refused' in prepared)
|
|
118
120
|
return prepared;
|
|
119
121
|
const { transfer } = parse(prepared.text);
|
|
120
|
-
const posted = await fetch(transfer.url, {
|
|
122
|
+
const posted = await fetch(relayed(transfer.url), {
|
|
121
123
|
method: 'POST',
|
|
122
124
|
headers: { 'content-type': 'text/plain; charset=utf-8' },
|
|
123
125
|
body: Buffer.concat([Buffer.from(`${transfer.operation}\n`), bytes]),
|
|
@@ -150,7 +152,7 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
150
152
|
const tool = message.method === 'tools/call' ? String(message.params.name) : null;
|
|
151
153
|
return send(message, tool !== null && (await writes(tool)) ? operationId() : null);
|
|
152
154
|
}
|
|
153
|
-
const answer = await fetch(new URL(path, origin), {
|
|
155
|
+
const answer = await fetch(relayed(new URL(path, origin)), {
|
|
154
156
|
method: 'POST',
|
|
155
157
|
headers: { 'content-type': 'application/json', authorization: `Bearer ${credential}` },
|
|
156
158
|
body: versioned(conversationId, text),
|
package/dist/clis.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { userInfo } from 'node:os';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { homedir, userInfo } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { kitHome } from './home.js';
|
|
7
7
|
const RUNNING_JOB = new Set(['running', 'paused']);
|
|
8
|
+
async function allowHouseInCodex() {
|
|
9
|
+
const rules = join(process.env.CODEX_HOME || join(homedir(), '.codex'), 'rules');
|
|
10
|
+
await mkdir(rules, { recursive: true });
|
|
11
|
+
await writeFile(join(rules, 'house.rules'), 'prefix_rule(pattern = ["house"], decision = "allow")\n');
|
|
12
|
+
}
|
|
13
|
+
async function nothing() { }
|
|
8
14
|
function threadStatus({ method, params }) {
|
|
9
15
|
if (method !== 'session/update' || params?.update?.sessionUpdate !== 'session_info_update')
|
|
10
16
|
return undefined;
|
|
@@ -36,6 +42,9 @@ export const CLIS = {
|
|
|
36
42
|
install: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh',
|
|
37
43
|
adapter: { package: '@agentclientprotocol/codex-acp', bin: 'codex-acp', executable: 'CODEX_PATH', env: {} },
|
|
38
44
|
args: [],
|
|
45
|
+
fullAccess: 'agent-full-access',
|
|
46
|
+
sessionMeta: {},
|
|
47
|
+
allowHouse: allowHouseInCodex,
|
|
39
48
|
signedIn: { command: ['login', 'status'] },
|
|
40
49
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
41
50
|
queues: false,
|
|
@@ -52,9 +61,12 @@ export const CLIS = {
|
|
|
52
61
|
package: '@agentclientprotocol/claude-agent-acp',
|
|
53
62
|
bin: 'claude-agent-acp',
|
|
54
63
|
executable: 'CLAUDE_CODE_EXECUTABLE',
|
|
55
|
-
env: { CLAUDE_CODE_ENTRYPOINT: 'claude-agent-acp' },
|
|
64
|
+
env: { CLAUDE_CODE_ENTRYPOINT: 'claude-agent-acp', IS_SANDBOX: '1' },
|
|
56
65
|
},
|
|
57
66
|
args: [],
|
|
67
|
+
fullAccess: 'bypassPermissions',
|
|
68
|
+
sessionMeta: { claudeCode: { options: { allowedTools: ['Bash(house:*)'] } } },
|
|
69
|
+
allowHouse: nothing,
|
|
58
70
|
signedIn: { command: ['auth', 'status'] },
|
|
59
71
|
login: { args: ['auth', 'login', '--claudeai'], code: 'collect', rejected: 'Invalid code' },
|
|
60
72
|
queues: false,
|
|
@@ -70,7 +82,10 @@ export const CLIS = {
|
|
|
70
82
|
minimum: '1.0.46',
|
|
71
83
|
install: 'curl -fsSL https://x.ai/cli/install.sh | bash',
|
|
72
84
|
adapter: null,
|
|
73
|
-
args: ['agent', '--no-leader', 'stdio'],
|
|
85
|
+
args: ['agent', '--always-approve', '--no-leader', 'stdio'],
|
|
86
|
+
fullAccess: null,
|
|
87
|
+
sessionMeta: {},
|
|
88
|
+
allowHouse: nothing,
|
|
74
89
|
signedIn: { initializeMeta: 'defaultAuthMethodId' },
|
|
75
90
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
76
91
|
queues: true,
|
package/dist/conversations.js
CHANGED
|
@@ -285,13 +285,15 @@ export class Conversations {
|
|
|
285
285
|
conversation.opening = bridge;
|
|
286
286
|
try {
|
|
287
287
|
await unlessAborted(createHowWeWork(bridge), conversation.killed.signal);
|
|
288
|
+
await CLIS[route.kind].allowHouse();
|
|
288
289
|
const adapter = await startAdapter(route.kind, cli, route.working_directory, app, (job) => this.job(conversation, job), bridge.env);
|
|
289
290
|
void adapter.exited.then(() => bridge.close());
|
|
290
291
|
const agent = adapter.connection.agent;
|
|
291
292
|
const cwd = route.working_directory;
|
|
293
|
+
const _meta = CLIS[route.kind].sessionMeta;
|
|
292
294
|
const opened = sessionId === null
|
|
293
|
-
? await agent.request('session/new', { cwd, mcpServers: [] })
|
|
294
|
-
: { ...(await agent.request('session/resume', { sessionId, cwd, mcpServers: [] })), sessionId };
|
|
295
|
+
? await agent.request('session/new', { cwd, mcpServers: [], _meta })
|
|
296
|
+
: { ...(await agent.request('session/resume', { sessionId, cwd, mcpServers: [], _meta })), sessionId };
|
|
295
297
|
const running = {
|
|
296
298
|
adapter,
|
|
297
299
|
bridge,
|
|
@@ -321,6 +323,7 @@ export class Conversations {
|
|
|
321
323
|
async launchSettings(running, route, offered) {
|
|
322
324
|
let options = offered;
|
|
323
325
|
const settings = [
|
|
326
|
+
['mode', route.mode ?? CLIS[route.kind].fullAccess],
|
|
324
327
|
['model', route.model],
|
|
325
328
|
['thought_level', route.effort],
|
|
326
329
|
];
|
package/dist/copies.js
CHANGED
|
@@ -8,6 +8,7 @@ import { kitHome } from './home.js';
|
|
|
8
8
|
import { operationId } from './operation.js';
|
|
9
9
|
import { refusalOf } from './refusals.js';
|
|
10
10
|
import { changed } from './translate.js';
|
|
11
|
+
import { relayed } from './relay.js';
|
|
11
12
|
const BRANCH = 'main';
|
|
12
13
|
const PROVENANCE = '_provenance';
|
|
13
14
|
const REPLICA_RETRY_MS = 30_000;
|
|
@@ -40,11 +41,11 @@ function sidecar(path) {
|
|
|
40
41
|
segments.splice(-1, 0, PROVENANCE);
|
|
41
42
|
return segments.join('/');
|
|
42
43
|
}
|
|
43
|
-
function rootOf(
|
|
44
|
-
return
|
|
44
|
+
function rootOf(handle) {
|
|
45
|
+
return `/rooms/${handle}`;
|
|
45
46
|
}
|
|
46
47
|
function relative(path) {
|
|
47
|
-
return path.replace(/^\/
|
|
48
|
+
return path.replace(/^\/rooms\/[^/]+\//, '');
|
|
48
49
|
}
|
|
49
50
|
function declaredWritable(path, copy) {
|
|
50
51
|
const deepest = (scopes) => Math.max(-1, ...scopes.filter((scope) => covers(path, scope)).map((scope) => scope.length));
|
|
@@ -176,7 +177,7 @@ export class LocalCopies {
|
|
|
176
177
|
async load() {
|
|
177
178
|
const home = join(kitHome(), 'local-copies');
|
|
178
179
|
const names = (await readdir(join(home, 'rooms')).catch(() => [])).filter((name) => name.endsWith('.json'));
|
|
179
|
-
for (const path of
|
|
180
|
+
for (const path of names.map((name) => join(home, 'rooms', name))) {
|
|
180
181
|
const text = await readFile(path, 'utf8').catch(() => null);
|
|
181
182
|
if (text === null)
|
|
182
183
|
continue;
|
|
@@ -188,7 +189,7 @@ export class LocalCopies {
|
|
|
188
189
|
await this.selecting;
|
|
189
190
|
return [...this.copies.values()]
|
|
190
191
|
.filter((copy) => copy.active)
|
|
191
|
-
.map((copy) => `${rootOf(copy)}: ${copy.repository}`)
|
|
192
|
+
.map((copy) => `${rootOf(copy.room_handle)}: ${copy.repository}`)
|
|
192
193
|
.sort();
|
|
193
194
|
}
|
|
194
195
|
select() {
|
|
@@ -198,12 +199,7 @@ export class LocalCopies {
|
|
|
198
199
|
}
|
|
199
200
|
async reselect() {
|
|
200
201
|
const { local_copy: chosen } = await this.house.post('/kit/local-copy/selection', {});
|
|
201
|
-
const selected = [
|
|
202
|
-
...(chosen?.rooms ?? [])
|
|
203
|
-
.filter((room) => room.room_handle !== null)
|
|
204
|
-
.map((room) => ({ key: `rooms/${room.room_ref}`, private: false, handle: room.room_handle })),
|
|
205
|
-
...(chosen?.private === true ? [{ key: 'private', private: true, handle: null }] : []),
|
|
206
|
-
];
|
|
202
|
+
const selected = (chosen?.rooms ?? []).flatMap((room) => room.room_handle === null ? [] : [{ key: `rooms/${room.room_ref}`, handle: room.room_handle }]);
|
|
207
203
|
const keys = new Set(selected.map((scope) => scope.key));
|
|
208
204
|
const work = [];
|
|
209
205
|
for (const key of this.copies.keys()) {
|
|
@@ -287,7 +283,7 @@ export class LocalCopies {
|
|
|
287
283
|
}).catch(logged);
|
|
288
284
|
}
|
|
289
285
|
async save(copy) {
|
|
290
|
-
const path = join(kitHome(), 'local-copies',
|
|
286
|
+
const path = join(kitHome(), 'local-copies', `${copy.key}.json`);
|
|
291
287
|
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
292
288
|
const temporary = `${path}.${process.pid}`;
|
|
293
289
|
await writeFile(temporary, JSON.stringify(copy), { mode: 0o600 });
|
|
@@ -295,12 +291,12 @@ export class LocalCopies {
|
|
|
295
291
|
this.copies.set(copy.key, copy);
|
|
296
292
|
return copy;
|
|
297
293
|
}
|
|
298
|
-
async read(
|
|
299
|
-
const prefixes = [rootOf(
|
|
294
|
+
async read(handle) {
|
|
295
|
+
const prefixes = [rootOf(handle)];
|
|
300
296
|
const discovery = await this.house.post('/kit/door/discover', { prefixes });
|
|
301
297
|
let bundle = await this.house.post('/kit/door/bootstrap', { prefixes });
|
|
302
298
|
if ('transfer' in bundle)
|
|
303
|
-
bundle = (await (await fetch(bundle.transfer.url)).json());
|
|
299
|
+
bundle = (await (await fetch(relayed(bundle.transfer.url))).json());
|
|
304
300
|
const [view] = bundle.scopes;
|
|
305
301
|
if (view === undefined)
|
|
306
302
|
return null;
|
|
@@ -343,13 +339,13 @@ export class LocalCopies {
|
|
|
343
339
|
return;
|
|
344
340
|
}
|
|
345
341
|
const discovery = await this.house.post('/kit/door/discover', {
|
|
346
|
-
prefixes: [rootOf(copy)],
|
|
342
|
+
prefixes: [rootOf(copy.room_handle)],
|
|
347
343
|
});
|
|
348
344
|
const authority = discovery.authorities.find((entry) => entry.room_ref === copy.room_ref);
|
|
349
345
|
if (authority === undefined)
|
|
350
346
|
return;
|
|
351
347
|
if (authority.managed_replication !== 'admitted') {
|
|
352
|
-
logged(`operation_denied: ${rootOf(copy)} is not synchronized to this Environment`);
|
|
348
|
+
logged(`operation_denied: ${rootOf(copy.room_handle)} is not synchronized to this Environment`);
|
|
353
349
|
return;
|
|
354
350
|
}
|
|
355
351
|
if (JSON.stringify([authority.operations.write, authority.protected_paths]) !==
|
|
@@ -360,12 +356,12 @@ export class LocalCopies {
|
|
|
360
356
|
await this.save(await this.caughtUp(copy));
|
|
361
357
|
}
|
|
362
358
|
async materialize(scope) {
|
|
363
|
-
const read = await this.read(
|
|
359
|
+
const read = await this.read(scope.handle);
|
|
364
360
|
if (read === null) {
|
|
365
|
-
logged(`House serves nothing of ${rootOf(
|
|
361
|
+
logged(`House serves nothing of ${rootOf(scope.handle)} to copy`);
|
|
366
362
|
return;
|
|
367
363
|
}
|
|
368
|
-
const repository =
|
|
364
|
+
const repository = join(localCopies(), 'rooms', read.anchor);
|
|
369
365
|
if (await exists(repository)) {
|
|
370
366
|
logged(`${repository} already exists and House Kit leaves it as it is`);
|
|
371
367
|
return;
|
|
@@ -377,7 +373,7 @@ export class LocalCopies {
|
|
|
377
373
|
try {
|
|
378
374
|
await git(staging, ['init', '-q', '-b', BRANCH]);
|
|
379
375
|
house = await importCommits(staging, [
|
|
380
|
-
{ message: `House ${rootOf(
|
|
376
|
+
{ message: `House ${rootOf(scope.handle)}`, from: null, files: read.writable },
|
|
381
377
|
]);
|
|
382
378
|
await git(staging, ['update-ref', `refs/heads/${BRANCH}`, house]);
|
|
383
379
|
await git(staging, ['reset', '-q', '--hard']);
|
|
@@ -391,7 +387,6 @@ export class LocalCopies {
|
|
|
391
387
|
}
|
|
392
388
|
await this.save({
|
|
393
389
|
key: scope.key,
|
|
394
|
-
private: scope.private,
|
|
395
390
|
room_ref: read.anchor,
|
|
396
391
|
room_handle: scope.handle,
|
|
397
392
|
repository,
|
|
@@ -407,11 +402,11 @@ export class LocalCopies {
|
|
|
407
402
|
});
|
|
408
403
|
}
|
|
409
404
|
async received(copy) {
|
|
410
|
-
const read = await this.read(copy);
|
|
405
|
+
const read = await this.read(copy.room_handle);
|
|
411
406
|
if (read === null)
|
|
412
407
|
return copy;
|
|
413
408
|
const house = await importCommits(copy.repository, [
|
|
414
|
-
{ message: `House ${rootOf(copy)} at position ${read.position}`, from: copy.house, replaces: true, files: read.writable },
|
|
409
|
+
{ message: `House ${rootOf(copy.room_handle)} at position ${read.position}`, from: copy.house, replaces: true, files: read.writable },
|
|
415
410
|
]);
|
|
416
411
|
const removed = copy.read_only.filter((path) => !read.readOnly.has(path));
|
|
417
412
|
const readOnly = await readOnlyPlaced(copy.repository, copy.read_only, read.readOnly, removed);
|
|
@@ -434,7 +429,7 @@ export class LocalCopies {
|
|
|
434
429
|
let answer;
|
|
435
430
|
try {
|
|
436
431
|
answer = await this.house.post('/kit/door/enumerate', {
|
|
437
|
-
prefixes: [rootOf(copy)],
|
|
432
|
+
prefixes: [rootOf(copy.room_handle)],
|
|
438
433
|
positions: [{ authority: copy.room_ref, position: copy.position, log_epoch: copy.log_epoch }],
|
|
439
434
|
});
|
|
440
435
|
}
|
|
@@ -484,7 +479,7 @@ export class LocalCopies {
|
|
|
484
479
|
let { house, manifests } = copy;
|
|
485
480
|
if (files.size > 0) {
|
|
486
481
|
house = await importCommits(copy.repository, [
|
|
487
|
-
{ message: `House ${rootOf(copy)} at position ${position}`, from: copy.house, files },
|
|
482
|
+
{ message: `House ${rootOf(copy.room_handle)} at position ${position}`, from: copy.house, files },
|
|
488
483
|
]);
|
|
489
484
|
manifests = { ...manifests, [house]: { parent: copy.house, files: revisions } };
|
|
490
485
|
}
|
|
@@ -515,7 +510,7 @@ export class LocalCopies {
|
|
|
515
510
|
copy = await this.save(without(copy));
|
|
516
511
|
}
|
|
517
512
|
if (!copy.active) {
|
|
518
|
-
return { refused: true, text: `${rootOf(copy)} is no longer synced to this Environment, so House takes no push from it` };
|
|
513
|
+
return { refused: true, text: `${rootOf(copy.room_handle)} is no longer synced to this Environment, so House takes no push from it` };
|
|
519
514
|
}
|
|
520
515
|
const listed = (await git(copy.repository, ['rev-list', '--topo-order', commit])).split('\n');
|
|
521
516
|
const base = listed.find((ancestor) => ancestor in copy.manifests);
|
|
@@ -549,7 +544,7 @@ export class LocalCopies {
|
|
|
549
544
|
return this.settle(submitted, caller);
|
|
550
545
|
}
|
|
551
546
|
async derived(copy, base, commit) {
|
|
552
|
-
const root = rootOf(copy);
|
|
547
|
+
const root = rootOf(copy.room_handle);
|
|
553
548
|
const before = await tree(copy.repository, base);
|
|
554
549
|
const after = await tree(copy.repository, commit);
|
|
555
550
|
const moved = movedPaths(copy.moves, before, after);
|
|
@@ -631,7 +626,7 @@ export class LocalCopies {
|
|
|
631
626
|
const push = copy.push;
|
|
632
627
|
const current = await this.save(await this.caughtUp(copy));
|
|
633
628
|
const house = await tree(copy.repository, current.house);
|
|
634
|
-
const root = rootOf(current);
|
|
629
|
+
const root = rootOf(current.room_handle);
|
|
635
630
|
if (!(await this.landed(copy.repository, push.changes, house, await tree(copy.repository, push.commit)))) {
|
|
636
631
|
await this.save(without(current));
|
|
637
632
|
return {
|
|
@@ -687,7 +682,7 @@ export class LocalCopies {
|
|
|
687
682
|
written = [
|
|
688
683
|
...written,
|
|
689
684
|
{
|
|
690
|
-
path: `${rootOf(copy)}/${path}`,
|
|
685
|
+
path: `${rootOf(copy.room_handle)}/${path}`,
|
|
691
686
|
revision: entry === undefined ? null : (revisionAt(copy, copy.house, path) ?? null),
|
|
692
687
|
content: entry === undefined ? null : await blobText(copy.repository, entry, path),
|
|
693
688
|
},
|
|
@@ -707,7 +702,7 @@ export class LocalCopies {
|
|
|
707
702
|
}
|
|
708
703
|
const edits = new Map([...files].filter(([path, content]) => (content === null ? selected.has(path) : selected.get(path) !== blobId(content))));
|
|
709
704
|
const received = [...files.keys()].filter((path) => revisionAt(copy, copy.house, path) === revisionAt(copy, push.house, path));
|
|
710
|
-
const accepted = `House ${rootOf(copy)} with ${push.commit} accepted`;
|
|
705
|
+
const accepted = `House ${rootOf(copy.room_handle)} with ${push.commit} accepted`;
|
|
711
706
|
const house = await importCommits(copy.repository, [
|
|
712
707
|
...(edits.size === 0 ? [] : [{ message: accepted, from: push.commit, files: edits }]),
|
|
713
708
|
{
|
package/dist/files.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { relayed } from './relay.js';
|
|
3
4
|
export async function placeFiles(house, directory, files, signal) {
|
|
4
5
|
const paths = [];
|
|
5
6
|
for (const file of files) {
|
|
6
7
|
const original = await house.post('/kit/conversation/originals/get', { version: file.version, download: true }, signal);
|
|
7
|
-
const answer = await fetch(original.download.url, { signal });
|
|
8
|
+
const answer = await fetch(relayed(original.download.url), { signal });
|
|
8
9
|
if (!answer.ok)
|
|
9
10
|
throw new Error(`${file.name} did not download: ${answer.status}`);
|
|
10
11
|
const folder = join(directory, '.house', 'files', original.message, file.version);
|
package/dist/instructions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HOW_WE_WORK } from './skills.js';
|
|
2
2
|
const HOUSE_LINE = 'Work with House through the `house` CLI: run `house --help`.';
|
|
3
3
|
const FILE_LINE = 'To give the User a file or a page, upload it with `house upload_attachment` and link it; never start a server.';
|
|
4
|
-
const READ_LINE = 'Before working in a Room, read `/private/rooms/<handle>/AGENTS.md`.';
|
|
4
|
+
const READ_LINE = 'Before working in a Room, read `/rooms/private/rooms/<handle>/AGENTS.md`.';
|
|
5
5
|
const COPIES_LINE = 'House Local copies; commit, then `house push`:';
|
|
6
6
|
const UNREADABLE = /^(?:\S+: )?(?:path_not_found|room_not_found|operation_denied)\b/;
|
|
7
7
|
const FAILED = /^exit: [1-9]\d*$/;
|
package/dist/mcp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { KIT_VERSION } from './clis.js';
|
|
3
|
+
import { relayed } from './relay.js';
|
|
3
4
|
const MCP_PROTOCOL_VERSION = '2026-07-28';
|
|
4
5
|
export const UNREACHABLE = 'House is unreachable; call again in a minute';
|
|
5
6
|
export function rpc(method, params) {
|
|
@@ -21,7 +22,7 @@ export function mcpBody(message, operation) {
|
|
|
21
22
|
}
|
|
22
23
|
export async function callHouse(origin, credential, message, operation = null, signal) {
|
|
23
24
|
const tool = message.method === 'tools/call' ? String(message.params.name) : null;
|
|
24
|
-
const answer = await fetch(new URL('/', origin), {
|
|
25
|
+
const answer = await fetch(relayed(new URL('/', origin)), {
|
|
25
26
|
method: 'POST',
|
|
26
27
|
headers: {
|
|
27
28
|
'content-type': 'application/json',
|
package/dist/relay.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function relayed(target) {
|
|
2
|
+
const url = new URL(target);
|
|
3
|
+
const relay = process.env.HOUSE_KIT_RELAY;
|
|
4
|
+
if (relay === undefined || relay === '')
|
|
5
|
+
return url;
|
|
6
|
+
const through = new URL(`${url.host}${url.pathname}${url.search}`, `${relay}/`);
|
|
7
|
+
if (url.protocol === 'wss:' || url.protocol === 'ws:')
|
|
8
|
+
through.protocol = 'wss:';
|
|
9
|
+
return through;
|
|
10
|
+
}
|
package/dist/secret-input.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { retryDelay } from './api.js';
|
|
1
|
+
import { HouseRefusal, retryDelay } from './api.js';
|
|
2
2
|
export async function holdSecretInput(house, subject, steps, waiting) {
|
|
3
3
|
for (let failures = 0; waiting();) {
|
|
4
4
|
try {
|
|
@@ -10,6 +10,8 @@ export async function holdSecretInput(house, subject, steps, waiting) {
|
|
|
10
10
|
failures = 0;
|
|
11
11
|
}
|
|
12
12
|
catch (error) {
|
|
13
|
+
if (error instanceof HouseRefusal && error.status < 500)
|
|
14
|
+
return null;
|
|
13
15
|
process.stderr.write(`kit: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
14
16
|
await new Promise((resolve) => setTimeout(resolve, retryDelay(failures++)));
|
|
15
17
|
}
|
package/dist/sign-in.js
CHANGED
|
@@ -31,8 +31,9 @@ export class SignIns {
|
|
|
31
31
|
this.running.add(input.input_id);
|
|
32
32
|
this.changed();
|
|
33
33
|
this.login(input)
|
|
34
|
-
.then(() => this.agents.
|
|
34
|
+
.then(() => this.agents.recheck(input.cli))
|
|
35
35
|
.then(() => this.house.deliver(`/kit/inputs/${input.input_id}/ack`, {}))
|
|
36
|
+
.then(() => this.agents.reread(input.cli))
|
|
36
37
|
.catch(logged)
|
|
37
38
|
.finally(() => {
|
|
38
39
|
this.running.delete(input.input_id);
|
package/dist/skills.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { KIT_VERSION } from './clis.js';
|
|
7
7
|
import { kitHome, readConfiguration, readHome, writeHome } from './home.js';
|
|
8
8
|
import { refusalOf } from './refusals.js';
|
|
9
|
-
export const HOW_WE_WORK = '/private/how-we-work.md';
|
|
9
|
+
export const HOW_WE_WORK = '/rooms/private/how-we-work.md';
|
|
10
10
|
const SKILL_SET = fileURLToPath(new URL('../skills/', import.meta.url));
|
|
11
11
|
const HOW_WE_WORK_TEXT = readFileSync(new URL('../how-we-work.md', import.meta.url), 'utf8');
|
|
12
12
|
const RECORD = 'skill-set.json';
|
package/dist/stream.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { retryDelay } from './api.js';
|
|
2
2
|
import { readEnrolment } from './home.js';
|
|
3
|
+
import { relayed } from './relay.js';
|
|
3
4
|
const SUBPROTOCOL = 'house.kit.stream.1';
|
|
4
5
|
function streamUrl(house) {
|
|
5
6
|
const url = new URL('/kit/stream', house);
|
|
@@ -11,7 +12,7 @@ export function holdStream(handlers) {
|
|
|
11
12
|
let failures = 0;
|
|
12
13
|
const connect = async () => {
|
|
13
14
|
const { house, credential } = (await readEnrolment());
|
|
14
|
-
const opening = new WebSocket(streamUrl(house), {
|
|
15
|
+
const opening = new WebSocket(relayed(streamUrl(house)), {
|
|
15
16
|
protocols: [SUBPROTOCOL],
|
|
16
17
|
headers: { authorization: `Bearer ${credential}` },
|
|
17
18
|
});
|