@agentshouse/kit 0.1.0-alpha.28 → 0.1.0-alpha.29

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
@@ -15,13 +15,13 @@ import { bridgedFrame, openBridge } from "./bridge.js";
15
15
  import { transferBytes } from "./bytes.js";
16
16
  import { assistantBlocks } from "./blocks.js";
17
17
  import { retryConversationFiles } from "./conversation-originals.js";
18
- import { openAssistantDrafts } from "./draft.js";
19
18
  import { localRequest, reconcileInbound } from "./inbound.js";
20
19
  import { localFiles } from "./local-files.js";
21
20
  import { answerText, answerYaml, editRefusal } from "./mcp.js";
22
21
  import { inheritedEnvironment } from "./command.js";
23
22
  import { sourceEntries } from "./google/entries.js";
24
23
  import { watchKitWork } from "./kit-work-stream.js";
24
+ import { finalPlan } from "./plan.js";
25
25
  import { coalescedTask, residentWake } from "./tasks.js";
26
26
  import {
27
27
  applyWorkingCopies,
@@ -56,7 +56,6 @@ import {
56
56
  acquireOneRun,
57
57
  acquireOneResumption,
58
58
  admitInteraction,
59
- admitPlanSnapshot,
60
59
  awaitSecretInput,
61
60
  rejectResumption,
62
61
  callHouseMcp,
@@ -78,7 +77,6 @@ import {
78
77
  reportSessions,
79
78
  serveResident,
80
79
  sessionAuthority,
81
- streamAssistantDraft,
82
80
  updateDrain,
83
81
  } from "./house.js";
84
82
  import { operationId } from "./operation.js";
@@ -87,6 +85,7 @@ import { readSettlements, removeSettlement, writeSettlement } from "./settlement
87
85
  import { howWeWorkOwed, noteHowWeWork, reconcileSkillSet } from "./skill-set.js";
88
86
  import { writeText } from "./state.js";
89
87
  import { failedReport, terminalReport } from "./terminal.js";
88
+ import { kitStreamWriter, turnFrames } from "./turn-frames.js";
90
89
  import {
91
90
  createSession,
92
91
  destroySession,
@@ -102,7 +101,7 @@ const PASS_REACHES = ["commands", "runs"];
102
101
 
103
102
  async function dispatchSeal(context, bound) {
104
103
  try {
105
- await acceptAssistantText(context.session, bound.session, bound.sealed);
104
+ await acceptAssistantText(context.session, bound.session, bound.sealed, bound.plan);
106
105
  bound.stopped.textAccepted = true;
107
106
  } catch (refusal) {
108
107
  if (!(refusal instanceof KitRefusal)) throw refusal;
@@ -217,7 +216,7 @@ async function completeOwnerStop(context, bound, requestedAt) {
217
216
  const destroyed = await destroySession(context.runtime, bound.tmux);
218
217
  let failure = null;
219
218
  try {
220
- await observeSessionStop(context.session, bound.session);
219
+ await observeSessionStop(context.session, bound.session, false, bound.plan);
221
220
  } catch (refusal) {
222
221
  if (undecided(context, refusal)) return { destroyed, confirmed: false, failure: null };
223
222
  if (!(refusal instanceof KitRefusal)) throw refusal;
@@ -230,7 +229,7 @@ async function completeOwnerStop(context, bound, requestedAt) {
230
229
 
231
230
  async function settleReported(context, bound) {
232
231
  try {
233
- await reportRunOutcome(context.session, bound.run, bound.reported);
232
+ await reportRunOutcome(context.session, bound.run, bound.reported, bound.plan);
234
233
  return { settled: true, failure: null };
235
234
  } catch (refusal) {
236
235
  if (undecided(context, refusal)) return { settled: false, failure: null };
@@ -359,25 +358,23 @@ async function sessionPush(context, bound, authority, frame, signal) {
359
358
  async function openRunBridge(context, bound, authority, start) {
360
359
  const ended = new AbortController();
361
360
  const handed = (act) => endOnEpochReport(ended, act);
362
- const drafts = openAssistantDrafts({
363
- send: handed((blocks) => streamAssistantDraft(context.session, bound.session, blocks)),
364
- });
361
+ const frames = turnFrames(context.stream);
365
362
  const bridge = await openBridge({
366
363
  socket: bound.socket,
367
364
  nonce: bound.nonce,
368
365
  signal: AbortSignal.any([context.signal, ended.signal]),
369
366
  mutates: mutatingCall(await toolMutations(context.session, context.signal)),
370
367
  alive: () => sessionPresent(context.runtime, bound.tmux),
371
- draft: (text) => drafts.update(text),
368
+ draft: (view) => frames.draft(view),
369
+ plan: (snapshot) => frames.plan(snapshot),
372
370
  start,
373
371
  question: handed((batch) => admitInteraction(context.session, bound.session, batch)),
374
- plan: handed((snapshot) => admitPlanSnapshot(context.session, bound.session, snapshot)),
375
372
  howWeWork: () =>
376
373
  noteHowWeWork(context.directory).catch((error) =>
377
374
  reported(`How-we-work note unwritable (${error.code ?? "unknown"})`),
378
375
  ),
379
- interrupt: handed((interrupt, outcome) =>
380
- reportInterruptOutcome(context.session, interrupt, outcome),
376
+ interrupt: handed((interrupt, outcome, plan) =>
377
+ reportInterruptOutcome(context.session, interrupt, outcome, finalPlan(plan)),
381
378
  ),
382
379
  secret: (interaction, ceremony) => awaitSecretInput(context.session, interaction, ceremony),
383
380
  settle: handed((interaction, outcome) => settleInteraction(context, interaction, outcome)),
@@ -393,11 +390,11 @@ async function openRunBridge(context, bound, authority, start) {
393
390
  signal,
394
391
  ), context.session.dependencies, signal)(frame)),
395
392
  });
396
- return { bridge, drafts, ended };
393
+ return { bridge, frames, ended };
397
394
  }
398
395
 
399
396
  async function superviseBound(context, bound, opened, start) {
400
- const { bridge, drafts, ended } = opened;
397
+ const { bridge, ended } = opened;
401
398
  const spawn = { committed: false };
402
399
  let unsupervised = null;
403
400
  let destroyed = false;
@@ -411,24 +408,20 @@ async function superviseBound(context, bound, opened, start) {
411
408
  unsupervised = { code: refusal.code, detail: refusal.message };
412
409
  } finally {
413
410
  try {
414
- try {
415
- retryFiles(context);
416
- await settleFiles(context);
417
- for (const interaction of bridge.unsettledInteractions()) {
418
- await settleInteraction(context, interaction, "expired");
419
- }
420
- } finally {
421
- bridge.stopRecovery();
422
- if (owner === null) {
423
- try {
424
- destroyed = await grantExitGrace(context.runtime, bound.tmux);
425
- } finally {
426
- await bridge.close();
427
- }
428
- }
411
+ retryFiles(context);
412
+ await settleFiles(context);
413
+ for (const interaction of bridge.unsettledInteractions()) {
414
+ await settleInteraction(context, interaction, "expired");
429
415
  }
430
416
  } finally {
431
- drafts.close();
417
+ bridge.stopRecovery();
418
+ if (owner === null) {
419
+ try {
420
+ destroyed = await grantExitGrace(context.runtime, bound.tmux);
421
+ } finally {
422
+ await bridge.close();
423
+ }
424
+ }
432
425
  }
433
426
  }
434
427
  ended.signal.throwIfAborted();
@@ -444,6 +437,7 @@ async function superviseBound(context, bound, opened, start) {
444
437
  destroyed,
445
438
  stopped: null,
446
439
  };
440
+ bound.plan = finalPlan(bridge.plan());
447
441
  if (owner !== null) {
448
442
  const finished = await completeOwnerStop(context, bound, owner.requestedAt);
449
443
  return {
@@ -495,12 +489,12 @@ async function settleStopped(context, bound) {
495
489
  }
496
490
  let failure = stopped.failure;
497
491
  try {
498
- await observeSessionStop(context.session, bound.session, stopped.textAccepted);
492
+ await observeSessionStop(context.session, bound.session, stopped.textAccepted, bound.plan);
499
493
  } catch (refusal) {
500
494
  if (undecided(context, refusal)) return unresolved(null);
501
495
  if (!(refusal instanceof KitRefusal) || refusal.code !== "conversation_final_text_pending") throw refusal;
502
496
  failure ??= { code: refusal.code, detail: refusal.message };
503
- await observeSessionStop(context.session, bound.session);
497
+ await observeSessionStop(context.session, bound.session, false, bound.plan);
504
498
  }
505
499
  const report = stoppedReport(bound);
506
500
  if (report !== null) await removeContinuation(context.directory, bound.run);
@@ -560,6 +554,7 @@ async function prepareAssignment(context, assignment, resumed) {
560
554
  ownerStop: null,
561
555
  sealed: null,
562
556
  reported: null,
557
+ plan: null,
563
558
  };
564
559
  const authority = { token: assignment.session.token, expiresAt: assignment.session.expiresAt };
565
560
  const start = {
@@ -642,7 +637,7 @@ async function superviseAssignment(context, assignment, resumed = false) {
642
637
  },
643
638
  command: context.launcher,
644
639
  });
645
- await observeSessionStart(context.session, bound.session);
640
+ opened.frames.identify(await observeSessionStart(context.session, bound.session));
646
641
  });
647
642
  }
648
643
 
@@ -661,7 +656,7 @@ async function superviseRestored(context, binding, authority) {
661
656
  const opened = await openRunBridge(context, binding, authority);
662
657
  return superviseBound(context, binding, opened, async (spawn) => {
663
658
  spawn.committed = true;
664
- await observeSessionStart(context.session, binding.session);
659
+ opened.frames.identify(await observeSessionStart(context.session, binding.session));
665
660
  });
666
661
  }
667
662
 
@@ -848,6 +843,7 @@ async function workerContext(options, dependencies, resident = false) {
848
843
  home: dependencies.home ?? homedir(),
849
844
  files: { originals: [], failures: [] },
850
845
  commands: residentWake(),
846
+ stream: kitStreamWriter(),
851
847
  epochReport: null,
852
848
  agentRefresh: Promise.resolve(),
853
849
  configurationPath: options.configuration,
@@ -920,7 +916,12 @@ export async function runWorkers(options, dependencies = {}) {
920
916
  const watching = (dependencies.watchKitWork ?? watchKitWork)(
921
917
  session,
922
918
  AbortSignal.any([signal, watcher.signal]),
923
- { available: commanded, changed: () => {} },
919
+ {
920
+ available: commanded,
921
+ changed: () => {},
922
+ opened: context.stream.opened,
923
+ closed: context.stream.closed,
924
+ },
924
925
  dependencies,
925
926
  ).catch((error) => reported(`control stream ended: ${error?.code ?? error?.message ?? error}`));
926
927
  try {
@@ -1036,6 +1037,7 @@ export async function runResident(options, dependencies = {}) {
1036
1037
  ),
1037
1038
  (source) =>
1038
1039
  sources === null ? Promise.reject(new KitRefusal("kit_resident_unavailable")) : sources.capture(source),
1040
+ context.stream,
1039
1041
  );
1040
1042
  const serve = async (signal) => {
1041
1043
  context.busy = true;
@@ -1160,7 +1162,13 @@ export async function runResident(options, dependencies = {}) {
1160
1162
  const watching = (dependencies.watchKitWork ?? watchKitWork)(
1161
1163
  context.session,
1162
1164
  AbortSignal.any([signal, watcher.signal]),
1163
- { available, changed },
1165
+ {
1166
+ available,
1167
+ changed,
1168
+ opened: context.stream.opened,
1169
+ closed: context.stream.closed,
1170
+ viewed: (frame) => served.forward(frame),
1171
+ },
1164
1172
  dependencies,
1165
1173
  ).catch(fail);
1166
1174
  try {