@agentshouse/kit 0.1.0-alpha.17 → 0.1.0-alpha.19

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/src/worker.js CHANGED
@@ -10,15 +10,16 @@ import {
10
10
  } from "./agents.js";
11
11
  import { readBindings, removeBinding, writeBinding } from "./binding.js";
12
12
  import { readContinuation, readContinuationRuns, writeContinuation, removeContinuation } from "./continuation.js";
13
- import { openBridge } from "./bridge.js";
13
+ import { bridgedFrame, 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
17
  import { localRequest, reconcileInbound } from "./inbound.js";
18
18
  import { localFiles } from "./local-files.js";
19
+ import { answerText, answerYaml } from "./mcp.js";
19
20
  import { inheritedEnvironment } from "./command.js";
20
21
  import { watchKitWork } from "./kit-work-stream.js";
21
- import { applyWorkingCopies, workingCopyPaths } from "./working-copy.js";
22
+ import { applyWorkingCopies, ownerCaller, pushWorkingCopy, workingCopyPaths } from "./working-copy.js";
22
23
  import { readConfiguration } from "./configuration.js";
23
24
  import { launchBase, readyLaunchDirectory } from "./directory.js";
24
25
  import {
@@ -32,8 +33,9 @@ import {
32
33
  WORKER_HEARTBEAT_MS,
33
34
  WORKER_INHERITED_ENVIRONMENT,
34
35
  WORKER_SUPERVISION_POLL_MS,
36
+ WORKING_COPY_PUSH_METHOD,
35
37
  } from "./contract.js";
36
- import { EpochReportRequired, houseFailed, KitRefusal } from "./refusal.js";
38
+ import { EpochReportRequired, formatCommandError, houseFailed, KitRefusal } from "./refusal.js";
37
39
  import {
38
40
  acceptAssistantText,
39
41
  acquireCommands,
@@ -270,6 +272,52 @@ function endOnEpochReport(ended, act) {
270
272
  };
271
273
  }
272
274
 
275
+ function refusedWith(text) {
276
+ const conflicts = answerYaml(text.slice(text.indexOf("\n") + 1))?.conflicts;
277
+ const current = Array.isArray(conflicts)
278
+ ? conflicts.map(({ path, state, revision }) =>
279
+ state === "absent" ? { path, revision: null, content: null } : { path, revision },
280
+ )
281
+ : null;
282
+ return { refused: { code: text.split(":")[0].trim(), text, current } };
283
+ }
284
+
285
+ function acceptedWith(text) {
286
+ const written = answerYaml(text).results.map(({ path, op, revision }) =>
287
+ op === "remove" ? { path, revision: null, content: null } : { path, revision },
288
+ );
289
+ return { refused: null, written };
290
+ }
291
+
292
+ async function sessionPush(context, bound, authority, frame, signal) {
293
+ const edit = async (body, sent) => {
294
+ const answer = await callHouseMcp(
295
+ context.session,
296
+ await sessionAuthority(context.session, bound.session, authority, sent),
297
+ bridgedFrame(
298
+ { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "edit", arguments: { changes: body.changes } } },
299
+ body.operation_id,
300
+ ),
301
+ sent,
302
+ );
303
+ if (answer.error !== undefined) return refusedWith(answer.error.message ?? "kit_house_refused");
304
+ const text = answerText(answer.result);
305
+ return answer.result?.isError === true ? refusedWith(text) : acceptedWith(text);
306
+ };
307
+ let outcome;
308
+ try {
309
+ outcome = await pushWorkingCopy(context, frame.params, { binding: { session: bound.session }, submit: edit }, signal);
310
+ } catch (refusal) {
311
+ if (!(refusal instanceof KitRefusal)) throw refusal;
312
+ outcome = { refused: true, text: refusal.message };
313
+ }
314
+ return {
315
+ jsonrpc: "2.0",
316
+ id: frame.id,
317
+ result: { content: [{ type: "text", text: outcome.text }], ...(outcome.refused ? { isError: true } : {}) },
318
+ };
319
+ }
320
+
273
321
  async function openRunBridge(context, bound, authority, start) {
274
322
  const ended = new AbortController();
275
323
  const handed = (act) => endOnEpochReport(ended, act);
@@ -291,8 +339,9 @@ async function openRunBridge(context, bound, authority, start) {
291
339
  ),
292
340
  secret: (interaction, ceremony) => awaitSecretInput(context.session, interaction, ceremony),
293
341
  settle: handed((interaction, outcome) => settleInteraction(context, interaction, outcome)),
294
- forward: handed((frame, signal) =>
295
- localFiles(async (sent) =>
342
+ forward: handed((frame, signal) => frame.method === WORKING_COPY_PUSH_METHOD
343
+ ? sessionPush(context, bound, authority, frame, signal)
344
+ : localFiles(async (sent) =>
296
345
  callHouseMcp(
297
346
  context.session,
298
347
  await sessionAuthority(context.session, bound.session, authority, signal),
@@ -960,7 +1009,16 @@ export async function runResident(options, dependencies = {}) {
960
1009
  const wake = residentWake();
961
1010
  const update = residentUpdate(context, wake);
962
1011
  context.update = update;
963
- const served = await serveResident(session, context.directory, options.signal, (serving) => update.request(serving));
1012
+ const served = await serveResident(
1013
+ session,
1014
+ context.directory,
1015
+ options.signal,
1016
+ (serving) => update.request(serving),
1017
+ (request, signal) =>
1018
+ pushWorkingCopy({ directory: context.directory, session }, request, ownerCaller(session), signal).catch(
1019
+ (error) => ({ refused: true, text: formatCommandError(error) }),
1020
+ ),
1021
+ );
964
1022
  const serve = async (signal) => {
965
1023
  context.busy = true;
966
1024
  const watcher = new AbortController();