@nuvin/session 0.3.0-rc.2 → 0.3.0-rc.4

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.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createSessionViewState,
3
3
  reduceSessionEvent
4
- } from "../chunk-B6S3CWQX.js";
4
+ } from "../chunk-UKDIV6I5.js";
5
5
  import {
6
6
  PROTOCOL_VERSION
7
- } from "../chunk-COSZ5DKO.js";
7
+ } from "../chunk-6QNR3TBF.js";
8
8
 
9
9
  // src/client/endpoint.ts
10
10
  var TerminalEndpointError = class extends Error {
@@ -442,6 +442,9 @@ function createHttpDataClient(options = {}) {
442
442
  }
443
443
 
444
444
  // src/client/session-client.ts
445
+ function cloneNonRootToolCallPath(toolCallPath) {
446
+ return toolCallPath && toolCallPath.length > 0 ? [...toolCallPath] : void 0;
447
+ }
445
448
  var SessionCommandError = class extends Error {
446
449
  constructor(code, message) {
447
450
  super(message);
@@ -633,7 +636,8 @@ function createSessionClient(transport) {
633
636
  })).then(() => {
634
637
  });
635
638
  },
636
- decideApproval(toolCallId, decision, comment, grantDir, parentToolCallId) {
639
+ decideApproval(toolCallId, decision, comment, grantDir, toolCallPath) {
640
+ const commandPath = cloneNonRootToolCallPath(toolCallPath);
637
641
  return sendCommand((id) => ({
638
642
  id,
639
643
  type: "decide-approval",
@@ -641,24 +645,30 @@ function createSessionClient(transport) {
641
645
  decision,
642
646
  ...comment !== void 0 ? { comment } : {},
643
647
  ...grantDir !== void 0 ? { grantDir } : {},
644
- ...parentToolCallId !== void 0 ? { parentToolCallId } : {}
648
+ ...commandPath !== void 0 ? { toolCallPath: commandPath } : {}
645
649
  })).then(() => {
646
650
  });
647
651
  },
648
- persistBashPermission(toolCallId, request, parentToolCallId) {
652
+ persistBashPermission(toolCallId, request, toolCallPath) {
653
+ const commandPath = cloneNonRootToolCallPath(toolCallPath);
649
654
  return sendCommand((id) => ({
650
655
  id,
651
656
  type: "persist-bash-permission",
652
657
  toolCallId,
653
- ...parentToolCallId !== void 0 ? { parentToolCallId } : {},
658
+ ...commandPath !== void 0 ? { toolCallPath: commandPath } : {},
654
659
  request
655
660
  })).then(toPersistBashPermissionResult);
656
661
  },
657
- answerQuestion(questionId, answers) {
658
- return sendCommand((id) => ({ id, type: "answer-question", questionId, answers })).then(
659
- () => {
660
- }
661
- );
662
+ answerQuestion(questionId, answers, toolCallPath) {
663
+ const commandPath = cloneNonRootToolCallPath(toolCallPath);
664
+ return sendCommand((id) => ({
665
+ id,
666
+ type: "answer-question",
667
+ questionId,
668
+ answers,
669
+ ...commandPath !== void 0 ? { toolCallPath: commandPath } : {}
670
+ })).then(() => {
671
+ });
662
672
  },
663
673
  abort() {
664
674
  return sendCommand((id) => ({ id, type: "abort" })).then(() => {
@@ -18,9 +18,9 @@ export interface SessionClient {
18
18
  /** Raw frame hook for UI side-effects (e.g. close modals on state-reset). */
19
19
  onEvent(listener: (event: ServerEvent) => void): () => void;
20
20
  submit(input: AgentInput, opts: ClientSubmitOptions): Promise<void>;
21
- decideApproval(toolCallId: string, decision: "a" | "n" | "y", comment?: string, grantDir?: string, parentToolCallId?: string): Promise<void>;
22
- persistBashPermission(toolCallId: string, request: PersistBashPermissionRequest, parentToolCallId?: string): Promise<PersistBashPermissionResult>;
23
- answerQuestion(questionId: string, answers: AskUserAnswers): Promise<void>;
21
+ decideApproval(toolCallId: string, decision: "a" | "n" | "y", comment?: string, grantDir?: string, toolCallPath?: readonly string[]): Promise<void>;
22
+ persistBashPermission(toolCallId: string, request: PersistBashPermissionRequest, toolCallPath?: readonly string[]): Promise<PersistBashPermissionResult>;
23
+ answerQuestion(questionId: string, answers: AskUserAnswers, toolCallPath?: readonly string[]): Promise<void>;
24
24
  abort(): Promise<void>;
25
25
  /** Cancel a queued (not-yet-running) message by its `turn-status.queued` id. */
26
26
  dequeueMessage(queuedId: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"session-client.d.ts","sourceRoot":"","sources":["../../src/client/session-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-client.d.ts","sourceRoot":"","sources":["../../src/client/session-client.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import type { AgentEvent, AskUserAnswers, AskUserQuestionRequest, Message, ToolPermissionClass, ToolRuntimeDispatchDecision, ToolUseBlock } from "@nuvin/agent-core/shared";
2
- import type { BashPolicyApprovalDiagnostics, DirAccessRequest, PersistBashPermissionHandlerResult, PersistBashPermissionRequest, WorkflowBashPolicySummary } from "../protocol/types.ts";
1
+ import type { AgentEvent, AskUserAnswers, AskUserQuestionRequest, Message, ToolExecutionScope, ToolPermissionClass, ToolRuntimeDispatchDecision, ToolUseBlock } from "@nuvin/agent-core/shared";
2
+ import type { BashPolicyApprovalDiagnostics, DirAccessRequest, PersistBashPermissionHandlerResult, PersistBashPermissionRequest, WireAgentEventScope, WorkflowBashPolicySummary } from "../protocol/types.ts";
3
3
  import type { TuiMessage } from "../state/messages.ts";
4
4
  /** UI-safe transcript projection of runtime LoadedSession. */
5
5
  export type SessionLoadedPayload = {
@@ -8,18 +8,14 @@ export type SessionLoadedPayload = {
8
8
  messages: TuiMessage[];
9
9
  };
10
10
  /**
11
- * Optional context describing the delegated child agent that produced the
12
- * event/tool-call. Set by the delegation wrapping in `root.tsx` so the UI
13
- * can nest sub-agent activity under the originating `AssignTask`
14
- * tool call. Coordinator (root) callbacks omit it.
11
+ * Convert normalized host scope to protocol scope. Root activity stays
12
+ * unscoped on the wire; nested paths are copied so caller mutation cannot
13
+ * change a routed event or interaction after publication.
15
14
  */
16
- export type DelegationScope = {
17
- agentId: string;
18
- parentToolCallId: string;
19
- };
15
+ export declare function toWireSessionScope(scope: ToolExecutionScope): WireAgentEventScope | undefined;
20
16
  export type AgentEventPayload = {
21
17
  event: AgentEvent;
22
- scope?: DelegationScope;
18
+ scope?: WireAgentEventScope;
23
19
  };
24
20
  export type ToolCallRequest = {
25
21
  agentId: string;
@@ -27,8 +23,8 @@ export type ToolCallRequest = {
27
23
  bashPolicy?: BashPolicyApprovalDiagnostics;
28
24
  /** Set by the runtime pre-check when the call's path is outside allowedDirs. */
29
25
  dirAccess?: DirAccessRequest;
30
- parentToolCallId?: string;
31
26
  permissionClass?: ToolPermissionClass;
27
+ toolCallPath?: readonly string[];
32
28
  persistBashPermission?: (request: PersistBashPermissionRequest) => Promise<PersistBashPermissionHandlerResult>;
33
29
  toolCall: ToolUseBlock;
34
30
  workflowBashPolicy?: WorkflowBashPolicySummary;
@@ -36,7 +32,7 @@ export type ToolCallRequest = {
36
32
  export type AgentEventListener = (payload: AgentEventPayload) => void;
37
33
  export type SessionLoadedListener = (loaded: SessionLoadedPayload) => void;
38
34
  export type ToolCallDecider = (request: ToolCallRequest) => Promise<ToolRuntimeDispatchDecision> | ToolRuntimeDispatchDecision;
39
- export type UserQuestionHandler = (request: AskUserQuestionRequest, scope?: DelegationScope) => Promise<AskUserAnswers> | AskUserAnswers;
35
+ export type UserQuestionHandler = (request: AskUserQuestionRequest, scope?: WireAgentEventScope) => Promise<AskUserAnswers> | AskUserAnswers;
40
36
  /**
41
37
  * In-process channel between the Agent (constructed in `main()`) and the
42
38
  * React UI (`<App />`). Two distinct shapes:
@@ -51,10 +47,10 @@ export declare class AgentChannel {
51
47
  private readonly emitter;
52
48
  private decider;
53
49
  private questionHandler;
54
- publishEvent(event: AgentEvent, scope?: DelegationScope): void;
50
+ publishEvent(event: AgentEvent, scope?: ToolExecutionScope): void;
55
51
  publishSessionLoaded(loaded: SessionLoadedPayload): void;
56
52
  requestToolDecision(request: ToolCallRequest): Promise<ToolRuntimeDispatchDecision> | ToolRuntimeDispatchDecision;
57
- requestUserQuestion(request: AskUserQuestionRequest, scope?: DelegationScope): Promise<AskUserAnswers>;
53
+ requestUserQuestion(request: AskUserQuestionRequest, scope?: ToolExecutionScope): Promise<AskUserAnswers>;
58
54
  onEvent(listener: AgentEventListener): () => void;
59
55
  onSessionLoaded(listener: SessionLoadedListener): () => void;
60
56
  setToolDecider(decider: ToolCallDecider | null): void;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-channel.d.ts","sourceRoot":"","sources":["../../src/controller/agent-channel.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,sBAAsB,EACtB,OAAO,EACP,mBAAmB,EACnB,2BAA2B,EAC3B,YAAY,EACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,kCAAkC,EAClC,4BAA4B,EAC5B,yBAAyB,EAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,6BAA6B,CAAC;IAC3C,gFAAgF;IAChF,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,qBAAqB,CAAC,EAAE,CACtB,OAAO,EAAE,4BAA4B,KAClC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACjD,QAAQ,EAAE,YAAY,CAAC;IACvB,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;AACtE,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;AAE3E,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,eAAe,KACrB,OAAO,CAAC,2BAA2B,CAAC,GAAG,2BAA2B,CAAC;AAExE,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,CAAC,EAAE,eAAe,KACpB,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAO9C;;;;;;;;;GASG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,eAAe,CAAoC;IAI3D,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI;IAI9D,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAIxD,mBAAmB,CACjB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,2BAA2B,CAAC,GAAG,2BAA2B;IAOrE,mBAAmB,CACjB,OAAO,EAAE,sBAAsB,EAC/B,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,cAAc,CAAC;IAS1B,OAAO,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAOjD,eAAe,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI;IAO5D,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IAIrD,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAAG,IAAI;CAG9D"}
1
+ {"version":3,"file":"agent-channel.d.ts","sourceRoot":"","sources":["../../src/controller/agent-channel.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,sBAAsB,EACtB,OAAO,EACP,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,YAAY,EACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,kCAAkC,EAClC,4BAA4B,EAC5B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,GAAG,SAAS,CAI7F;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,6BAA6B,CAAC;IAC3C,gFAAgF;IAChF,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,qBAAqB,CAAC,EAAE,CACtB,OAAO,EAAE,4BAA4B,KAClC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACjD,QAAQ,EAAE,YAAY,CAAC;IACvB,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;AACtE,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;AAE3E,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,eAAe,KACrB,OAAO,CAAC,2BAA2B,CAAC,GAAG,2BAA2B,CAAC;AAExE,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,CAAC,EAAE,mBAAmB,KACxB,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAO9C;;;;;;;;;GASG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,eAAe,CAAoC;IAI3D,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,kBAAkB,GAAG,IAAI;IAOjE,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAIxD,mBAAmB,CACjB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,2BAA2B,CAAC,GAAG,2BAA2B;IAerE,mBAAmB,CACjB,OAAO,EAAE,sBAAsB,EAC/B,KAAK,CAAC,EAAE,kBAAkB,GACzB,OAAO,CAAC,cAAc,CAAC;IAW1B,OAAO,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAOjD,eAAe,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI;IAO5D,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IAIrD,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAAG,IAAI;CAG9D"}
@@ -7,11 +7,16 @@ import {
7
7
  createSessionViewState,
8
8
  isToolAutoApprovedForMode,
9
9
  reduceSessionEvent,
10
- scopedToolKey
11
- } from "../chunk-B6S3CWQX.js";
10
+ sanitizeBoundedDisplayText,
11
+ scopedToolKey,
12
+ summarizeToolCallInput
13
+ } from "../chunk-UKDIV6I5.js";
12
14
 
13
15
  // src/controller/agent-channel.ts
14
16
  import { EventEmitter } from "events";
17
+ function toWireSessionScope(scope) {
18
+ return scope.toolCallPath.length === 0 ? void 0 : { agentId: scope.agentId, toolCallPath: [...scope.toolCallPath] };
19
+ }
15
20
  var NO_DECIDER_DECISION = {
16
21
  action: "reject",
17
22
  reason: "UI is not ready to handle tool calls."
@@ -22,7 +27,10 @@ var AgentChannel = class {
22
27
  questionHandler = null;
23
28
  // ---------- Agent-side (publishers) ----------
24
29
  publishEvent(event, scope) {
25
- this.emitter.emit("event", { event, scope });
30
+ this.emitter.emit("event", {
31
+ event,
32
+ scope: scope === void 0 ? void 0 : toWireSessionScope(scope)
33
+ });
26
34
  }
27
35
  publishSessionLoaded(loaded) {
28
36
  this.emitter.emit("session-loaded", loaded);
@@ -31,13 +39,23 @@ var AgentChannel = class {
31
39
  if (!this.decider) {
32
40
  return NO_DECIDER_DECISION;
33
41
  }
34
- return this.decider(request);
42
+ const { toolCallPath, ...rootRequest } = request;
43
+ const scope = toWireSessionScope({
44
+ agentId: request.agentId,
45
+ toolCallPath: toolCallPath ?? []
46
+ });
47
+ return this.decider({
48
+ ...rootRequest,
49
+ ...scope === void 0 ? {} : { toolCallPath: scope.toolCallPath }
50
+ });
35
51
  }
36
52
  requestUserQuestion(request, scope) {
37
53
  if (!this.questionHandler) {
38
54
  return Promise.reject(new Error("UI is not ready to handle user questions."));
39
55
  }
40
- return Promise.resolve(this.questionHandler(request, scope));
56
+ return Promise.resolve(
57
+ this.questionHandler(request, scope === void 0 ? void 0 : toWireSessionScope(scope))
58
+ );
41
59
  }
42
60
  // ---------- UI-side (subscribers) ----------
43
61
  onEvent(listener) {
@@ -94,8 +112,8 @@ function createSessionController(deps) {
94
112
  let activeTurnId;
95
113
  let activePolicyDenial;
96
114
  let hostBusyCount = 0;
97
- let questionCounter = 0;
98
115
  const listeners = /* @__PURE__ */ new Set();
116
+ const abortListeners = /* @__PURE__ */ new Set();
99
117
  const approvalResolvers = /* @__PURE__ */ new Map();
100
118
  const privateApprovals = /* @__PURE__ */ new Map();
101
119
  const persistenceInFlight = /* @__PURE__ */ new Set();
@@ -171,8 +189,8 @@ function createSessionController(deps) {
171
189
  });
172
190
  });
173
191
  deps.channel.setToolDecider((request) => {
174
- const { toolCall, agentId, parentToolCallId, dirAccess, bashPolicy } = request;
175
- const scope = parentToolCallId !== void 0 ? { agentId, parentToolCallId } : void 0;
192
+ const { toolCall, agentId, toolCallPath, dirAccess, bashPolicy } = request;
193
+ const scope = toolCallPath === void 0 ? void 0 : { agentId, toolCallPath: [...toolCallPath] };
176
194
  const approvalKey = request.approvalKey ?? `${agentId}:${toolCall.name}`;
177
195
  const permissionClass = request.permissionClass ?? "always-confirm";
178
196
  const hasBashPolicy = toolCall.name === "Bash" && bashPolicy !== void 0;
@@ -246,27 +264,30 @@ function createSessionController(deps) {
246
264
  activePolicyDenial = "Tool approval is unavailable in headless mode.";
247
265
  return { action: "reject", reason: activePolicyDenial };
248
266
  }
249
- const nickname = nicknameFor(parentToolCallId);
250
- const input = asJsonObject(toolCall.input);
267
+ const nickname = nicknameFor(toolCallPath);
268
+ const ancestorDisplay = buildAncestorDisplay(toolCallPath);
269
+ const rawInput = asJsonObject(toolCall.input);
270
+ const input = toolCall.name === "Program" ? typeof rawInput?.description === "string" ? { description: sanitizeBoundedDisplayText(rawInput.description) } : void 0 : rawInput;
251
271
  const descriptor = {
252
272
  agentId,
253
273
  permissionClass,
254
- summary: findToolSummary(toolCall.id, parentToolCallId),
274
+ summary: findToolSummary(toolCall.id, toolCallPath),
255
275
  toolCallId: toolCall.id,
256
276
  toolName: toolCall.name,
277
+ ...ancestorDisplay !== void 0 ? { ancestorDisplay } : {},
257
278
  ...dirAccess !== void 0 ? { dirAccess } : {},
258
279
  ...input !== void 0 ? { input } : {},
259
- ...parentToolCallId !== void 0 ? { parentToolCallId } : {},
280
+ ...toolCallPath !== void 0 ? { toolCallPath: [...toolCallPath] } : {},
260
281
  ...nickname !== void 0 ? { nickname } : {},
261
282
  ...!dirAccess && bashPolicy !== void 0 ? { bashPolicy } : {},
262
283
  ...request.workflowBashPolicy !== void 0 ? { workflowBashPolicy: request.workflowBashPolicy } : {}
263
284
  };
264
285
  return new Promise((resolve) => {
265
- const toolKey = scopedToolKey(parentToolCallId, toolCall.id);
286
+ const toolKey = scopedToolKey(toolCallPath, toolCall.id);
266
287
  approvalResolvers.set(toolKey, {
267
288
  resolve,
268
289
  toolCallId: toolCall.id,
269
- ...parentToolCallId !== void 0 ? { parentToolCallId } : {}
290
+ ...toolCallPath !== void 0 ? { toolCallPath: [...toolCallPath] } : {}
270
291
  });
271
292
  privateApprovals.set(toolKey, {
272
293
  approvalKey,
@@ -281,39 +302,69 @@ function createSessionController(deps) {
281
302
  activePolicyDenial = "Questions are unavailable in headless mode.";
282
303
  return Promise.reject(new Error(activePolicyDenial));
283
304
  }
284
- questionCounter += 1;
285
- const questionId = `question-${questionCounter}`;
305
+ const { questionId } = request;
306
+ const toolCallPath = scope?.toolCallPath;
286
307
  return new Promise((resolve, reject) => {
287
- questionResolvers.set(questionId, { resolve, reject });
288
- const nickname = nicknameFor(scope?.parentToolCallId);
308
+ const questionKey = scopedToolKey(toolCallPath, questionId);
309
+ questionResolvers.set(questionKey, {
310
+ resolve,
311
+ reject,
312
+ questionId,
313
+ ...toolCallPath !== void 0 ? { toolCallPath: [...toolCallPath] } : {}
314
+ });
315
+ const nickname = nicknameFor(toolCallPath);
289
316
  emit({
290
317
  type: "question-asked",
291
318
  question: {
292
319
  questionId,
293
320
  request,
294
321
  ...scope?.agentId !== void 0 ? { agentId: scope.agentId } : {},
295
- ...scope?.parentToolCallId !== void 0 ? { parentToolCallId: scope.parentToolCallId } : {},
322
+ ...toolCallPath !== void 0 ? { toolCallPath: [...toolCallPath] } : {},
296
323
  ...nickname !== void 0 ? { nickname } : {}
297
324
  }
298
325
  });
299
326
  });
300
327
  });
301
- function findToolSummary(toolCallId, parentToolCallId) {
302
- const messageId = state.messages.toolMessageIds[scopedToolKey(parentToolCallId, toolCallId)];
328
+ function findToolMessage(toolCallId, toolCallPath) {
329
+ const messageId = state.messages.toolMessageIds[scopedToolKey(toolCallPath, toolCallId)];
303
330
  const message = state.messages.messages.find((entry) => entry.id === messageId);
304
- return message?.role === "tool" ? message.summary : "";
331
+ return message?.role === "tool" ? message : void 0;
305
332
  }
306
- function nicknameFor(parentToolCallId) {
307
- if (!parentToolCallId) return void 0;
308
- const parentMessageId = state.messages.toolMessageIds[scopedToolKey(void 0, parentToolCallId)];
309
- const parentMessage = state.messages.messages.find((entry) => entry.id === parentMessageId);
310
- if (parentMessage?.role !== "tool") return void 0;
311
- const nickname = parentMessage.input?.nickname;
312
- return typeof nickname === "string" ? nickname : void 0;
333
+ function findToolSummary(toolCallId, toolCallPath) {
334
+ const summary = findToolMessage(toolCallId, toolCallPath)?.summary ?? "";
335
+ return sanitizeBoundedDisplayText(summary);
313
336
  }
314
- function findDescriptor(toolCallId, parentToolCallId) {
337
+ function nicknameFor(toolCallPath) {
338
+ if (!toolCallPath || toolCallPath.length === 0) return void 0;
339
+ const parentIndex = toolCallPath.length - 1;
340
+ const directAncestorId = toolCallPath[parentIndex];
341
+ if (directAncestorId === void 0) return void 0;
342
+ const parentMessage = findToolMessage(directAncestorId, toolCallPath.slice(0, parentIndex));
343
+ const nickname = parentMessage?.input?.nickname;
344
+ return typeof nickname === "string" ? sanitizeBoundedDisplayText(nickname) : void 0;
345
+ }
346
+ function buildAncestorDisplay(toolCallPath) {
347
+ if (!toolCallPath || toolCallPath.length === 0) return void 0;
348
+ const ancestors = [];
349
+ for (let index = 0; index < toolCallPath.length; index += 1) {
350
+ const toolCallId = toolCallPath[index];
351
+ if (toolCallId === void 0) return void 0;
352
+ const message = findToolMessage(toolCallId, toolCallPath.slice(0, index));
353
+ if (!message) return void 0;
354
+ const nickname = message.input?.nickname;
355
+ ancestors.push({
356
+ toolCallId,
357
+ toolName: message.toolName,
358
+ summary: message.toolName === "Program" ? summarizeToolCallInput(message.toolName, message.input ?? {}) : sanitizeBoundedDisplayText(message.summary),
359
+ ...typeof nickname === "string" ? { nickname: sanitizeBoundedDisplayText(nickname) } : {}
360
+ });
361
+ }
362
+ return ancestors;
363
+ }
364
+ function findDescriptor(toolCallId, toolCallPath) {
365
+ const toolKey = scopedToolKey(toolCallPath, toolCallId);
315
366
  return [state.approval.active, ...state.approval.pending].find(
316
- (entry) => entry?.toolCallId === toolCallId && entry.parentToolCallId === parentToolCallId
367
+ (entry) => entry !== null && scopedToolKey(entry.toolCallPath, entry.toolCallId) === toolKey
317
368
  );
318
369
  }
319
370
  function rejectAllPending(reason) {
@@ -322,7 +373,7 @@ function createSessionController(deps) {
322
373
  emit({
323
374
  type: "approval-settled",
324
375
  toolCallId: pending.toolCallId,
325
- ...pending.parentToolCallId === void 0 ? {} : { parentToolCallId: pending.parentToolCallId },
376
+ ...pending.toolCallPath === void 0 ? {} : { toolCallPath: [...pending.toolCallPath] },
326
377
  status: "rejected",
327
378
  by: "abort"
328
379
  });
@@ -330,9 +381,13 @@ function createSessionController(deps) {
330
381
  approvalResolvers.clear();
331
382
  privateApprovals.clear();
332
383
  persistenceInFlight.clear();
333
- for (const [questionId, { reject }] of questionResolvers) {
384
+ for (const { reject, questionId, toolCallPath } of questionResolvers.values()) {
334
385
  reject(new Error(reason));
335
- emit({ type: "question-settled", questionId });
386
+ emit({
387
+ type: "question-settled",
388
+ questionId,
389
+ ...toolCallPath === void 0 ? {} : { toolCallPath: [...toolCallPath] }
390
+ });
336
391
  }
337
392
  questionResolvers.clear();
338
393
  }
@@ -352,7 +407,8 @@ function createSessionController(deps) {
352
407
  if (closing || abortController || hostBusyCount > 0) return;
353
408
  const next = queue.shift();
354
409
  if (!next) return;
355
- if (!next.opts.silent) {
410
+ const coordinatorOwned = next.opts.submission?.origin === "coordinator";
411
+ if (!coordinatorOwned && !next.opts.silent) {
356
412
  emit(
357
413
  {
358
414
  type: "user-message",
@@ -366,6 +422,7 @@ function createSessionController(deps) {
366
422
  turnId: next.opts.turnId,
367
423
  allowSteering: next.opts.turnId === void 0,
368
424
  submission: next.opts.submission ?? { origin: "user" },
425
+ ...coordinatorOwned ? { coordinatorDisplayText: next.opts.displayText } : {},
369
426
  ...next.opts.signal === void 0 ? {} : { signal: next.opts.signal }
370
427
  });
371
428
  }
@@ -401,7 +458,12 @@ function createSessionController(deps) {
401
458
  }
402
459
  if (abortController !== controller || controller.signal.aborted) return [];
403
460
  for (const entry of accepted) {
404
- if (!entry.opts.silent) {
461
+ if (entry.opts.submission?.origin === "coordinator") {
462
+ emit({
463
+ type: "info",
464
+ message: sanitizeBoundedDisplayText(entry.opts.displayText)
465
+ });
466
+ } else if (!entry.opts.silent) {
405
467
  emit({
406
468
  type: "user-message",
407
469
  text: entry.opts.displayText,
@@ -409,7 +471,9 @@ function createSessionController(deps) {
409
471
  });
410
472
  }
411
473
  }
412
- return accepted.map((entry) => entry.input);
474
+ return accepted.map(
475
+ (entry) => entry.opts.submission?.origin === "coordinator" ? { input: entry.input, submission: entry.opts.submission } : entry.input
476
+ );
413
477
  };
414
478
  const previousTurnId = activeTurnId;
415
479
  if (opts.turnId !== void 0) activeTurnId = opts.turnId;
@@ -440,6 +504,12 @@ function createSessionController(deps) {
440
504
  }
441
505
  finalInput = guarded.prompt;
442
506
  }
507
+ if (opts.coordinatorDisplayText !== void 0) {
508
+ emit({
509
+ type: "info",
510
+ message: sanitizeBoundedDisplayText(opts.coordinatorDisplayText)
511
+ });
512
+ }
443
513
  const compactionSend = (text) => runTurn(text, {
444
514
  systemInitiated: true,
445
515
  allowSteering: opts.allowSteering,
@@ -542,23 +612,40 @@ function createSessionController(deps) {
542
612
  deps.channel.setToolDecider(null);
543
613
  deps.channel.setQuestionHandler(null);
544
614
  listeners.clear();
615
+ abortListeners.clear();
545
616
  }
546
617
  return {
547
618
  abort() {
548
619
  queue.length = 0;
549
- abortController?.abort(new Error("User aborted the current turn."));
620
+ const reason = new Error("User aborted the current turn.");
621
+ for (const listener of abortListeners) {
622
+ try {
623
+ listener(reason);
624
+ } catch {
625
+ }
626
+ }
627
+ abortController?.abort(reason);
550
628
  rejectAllPending("User aborted the current turn.");
551
629
  },
630
+ onAbort(listener) {
631
+ abortListeners.add(listener);
632
+ return () => abortListeners.delete(listener);
633
+ },
552
634
  attachHostHooks(hooks) {
553
635
  if (hooks.compaction) compaction = hooks.compaction;
554
636
  if (hooks.guardPrompt) guardPrompt = hooks.guardPrompt;
555
637
  },
556
- answerQuestion(questionId, answers) {
557
- const entry = questionResolvers.get(questionId);
638
+ answerQuestion(questionId, answers, toolCallPath) {
639
+ const questionKey = scopedToolKey(toolCallPath, questionId);
640
+ const entry = questionResolvers.get(questionKey);
558
641
  if (!entry) return false;
559
- questionResolvers.delete(questionId);
642
+ questionResolvers.delete(questionKey);
560
643
  entry.resolve(answers);
561
- emit({ type: "question-settled", questionId });
644
+ emit({
645
+ type: "question-settled",
646
+ questionId,
647
+ ...toolCallPath && toolCallPath.length > 0 ? { toolCallPath: [...toolCallPath] } : {}
648
+ });
562
649
  return true;
563
650
  },
564
651
  appendError(message) {
@@ -591,8 +678,8 @@ function createSessionController(deps) {
591
678
  emitTurnStatus();
592
679
  return true;
593
680
  },
594
- decideApproval(toolCallId, decision, comment, grantDir, parentToolCallId) {
595
- const toolKey = scopedToolKey(parentToolCallId, toolCallId);
681
+ decideApproval(toolCallId, decision, comment, grantDir, toolCallPath) {
682
+ const toolKey = scopedToolKey(toolCallPath, toolCallId);
596
683
  if (persistenceInFlight.has(toolKey)) {
597
684
  return false;
598
685
  }
@@ -600,7 +687,7 @@ function createSessionController(deps) {
600
687
  if (!pending) return false;
601
688
  approvalResolvers.delete(toolKey);
602
689
  persistenceInFlight.delete(toolKey);
603
- const descriptor = findDescriptor(toolCallId, parentToolCallId);
690
+ const descriptor = findDescriptor(toolCallId, toolCallPath);
604
691
  const privateApproval = privateApprovals.get(toolKey);
605
692
  privateApprovals.delete(toolKey);
606
693
  if (decision === "a" && descriptor && !descriptor.dirAccess) {
@@ -643,18 +730,21 @@ function createSessionController(deps) {
643
730
  emit({
644
731
  type: "approval-settled",
645
732
  toolCallId,
646
- ...parentToolCallId !== void 0 ? { parentToolCallId } : {},
733
+ ...toolCallPath && toolCallPath.length > 0 ? { toolCallPath: [...toolCallPath] } : {},
647
734
  status: rejected ? "rejected" : "approved",
648
735
  by: "client"
649
736
  });
650
737
  return true;
651
738
  },
652
- async persistBashPermission(toolCallId, request, parentToolCallId) {
653
- const toolKey = scopedToolKey(parentToolCallId, toolCallId);
739
+ async persistBashPermission(toolCallId, request, toolCallPath) {
740
+ const persistenceToolCallPath = toolCallPath && toolCallPath.length > 0 ? [...toolCallPath] : void 0;
741
+ const toolKey = scopedToolKey(persistenceToolCallPath, toolCallId);
654
742
  const descriptor = state.approval.active;
743
+ const descriptorKey = descriptor === null ? void 0 : scopedToolKey(descriptor.toolCallPath, descriptor.toolCallId);
655
744
  const privateApproval = privateApprovals.get(toolKey);
656
745
  const handler = privateApproval?.persistBashPermission;
657
- if (descriptor?.toolCallId !== toolCallId || descriptor.parentToolCallId !== parentToolCallId || descriptor.toolName !== "Bash" || descriptor.dirAccess !== void 0 || descriptor.bashPolicy?.persistence.eligible !== true || handler === void 0 || !approvalResolvers.has(toolKey)) {
746
+ const pending = approvalResolvers.get(toolKey);
747
+ if (descriptorKey !== toolKey || descriptor?.toolName !== "Bash" || descriptor.dirAccess !== void 0 || descriptor.bashPolicy?.persistence.eligible !== true || handler === void 0 || pending === void 0) {
658
748
  return void 0;
659
749
  }
660
750
  if (persistenceInFlight.has(toolKey)) {
@@ -676,13 +766,13 @@ function createSessionController(deps) {
676
766
  emitTurnStatus();
677
767
  try {
678
768
  const outcome = await handler(request);
769
+ const activeDescriptor = state.approval.active;
770
+ if (approvalResolvers.get(toolKey) !== pending || activeDescriptor === null || scopedToolKey(activeDescriptor.toolCallPath, activeDescriptor.toolCallId) !== toolKey || privateApprovals.get(toolKey) !== privateApproval) {
771
+ return void 0;
772
+ }
679
773
  if (outcome.status === "validation-error") {
680
774
  return outcome;
681
775
  }
682
- const pending = approvalResolvers.get(toolKey);
683
- if (pending === void 0 || state.approval.active?.toolCallId !== toolCallId || state.approval.active.parentToolCallId !== parentToolCallId || privateApprovals.get(toolKey) !== privateApproval) {
684
- return void 0;
685
- }
686
776
  approvalResolvers.delete(toolKey);
687
777
  privateApprovals.delete(toolKey);
688
778
  pending.resolve({
@@ -693,7 +783,7 @@ function createSessionController(deps) {
693
783
  emit({
694
784
  type: "approval-settled",
695
785
  toolCallId,
696
- ...parentToolCallId !== void 0 ? { parentToolCallId } : {},
786
+ ...persistenceToolCallPath === void 0 ? {} : { toolCallPath: [...persistenceToolCallPath] },
697
787
  status: "approved",
698
788
  by: "client"
699
789
  });
@@ -733,6 +823,13 @@ function createSessionController(deps) {
733
823
  publishMetrics(snapshot) {
734
824
  emit({ type: "metrics", snapshot });
735
825
  },
826
+ publishBackgroundAgentStatus(status) {
827
+ emit({
828
+ type: "background-agent-status",
829
+ ...status,
830
+ runningRunIds: [...status.runningRunIds]
831
+ });
832
+ },
736
833
  publishAuthFlow(flow) {
737
834
  emit({ type: "auth-flow", flow });
738
835
  },
@@ -787,7 +884,8 @@ function createSessionController(deps) {
787
884
  emitTurnStatus();
788
885
  return;
789
886
  }
790
- if (!opts.silent) {
887
+ const coordinatorOwned = opts.submission?.origin === "coordinator";
888
+ if (!coordinatorOwned && !opts.silent) {
791
889
  emit(
792
890
  {
793
891
  type: "user-message",
@@ -801,6 +899,7 @@ function createSessionController(deps) {
801
899
  turnId: opts.turnId,
802
900
  allowSteering: opts.turnId === void 0,
803
901
  submission: opts.submission ?? { origin: "user" },
902
+ ...coordinatorOwned ? { coordinatorDisplayText: opts.displayText } : {},
804
903
  ...opts.signal === void 0 ? {} : { signal: opts.signal }
805
904
  });
806
905
  }
@@ -809,5 +908,6 @@ function createSessionController(deps) {
809
908
  export {
810
909
  AgentChannel,
811
910
  createSessionController,
812
- formatBashPolicyDenial
911
+ formatBashPolicyDenial,
912
+ toWireSessionScope
813
913
  };