@gajae-code/coding-agent 0.12.21 → 0.13.0
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/CHANGELOG.md +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
|
@@ -45,6 +45,7 @@ export interface LocalProtocolOptions {
|
|
|
45
45
|
getArtifactsDir?: () => string | null;
|
|
46
46
|
isManagedDestination?: () => boolean;
|
|
47
47
|
getManagedLegacyLocalMigrationSource?: () => ManagedLegacyLocalMigrationSource | null;
|
|
48
|
+
getCredentialSessionId?: () => string | null;
|
|
48
49
|
getSessionId?: () => string | null;
|
|
49
50
|
}
|
|
50
51
|
|
package/src/main.ts
CHANGED
|
@@ -54,6 +54,9 @@ import {
|
|
|
54
54
|
createAgentSession,
|
|
55
55
|
discoverAuthStorage,
|
|
56
56
|
} from "./sdk";
|
|
57
|
+
import { processIncarnation } from "./sdk/broker/process-incarnation";
|
|
58
|
+
import { SessionIndex } from "./sdk/broker/session-index";
|
|
59
|
+
|
|
57
60
|
import type { AgentSession } from "./session/agent-session";
|
|
58
61
|
import { SessionMigrationBusyError } from "./session/internal/session-open-errors";
|
|
59
62
|
import {
|
|
@@ -264,7 +267,7 @@ export function resolveAcpStartupOptions(
|
|
|
264
267
|
...(parsed.plan ? ["--plan"] : []),
|
|
265
268
|
...(parsed.systemPrompt ? ["--system-prompt"] : []),
|
|
266
269
|
...(parsed.tmux ? ["--tmux"] : []),
|
|
267
|
-
...(parsed.tools
|
|
270
|
+
...(parsed.tools !== undefined ? ["--tools"] : []),
|
|
268
271
|
...(parsed.extensions?.length ? ["--extension"] : []),
|
|
269
272
|
...(parsed.unknownFlags.size > 0 ? [`unknown flags: ${[...parsed.unknownFlags.keys()].join(" ")}`] : []),
|
|
270
273
|
];
|
|
@@ -997,8 +1000,14 @@ export async function createSessionManager(
|
|
|
997
1000
|
// buildSessionOptions restores the session's model/thinking instead of
|
|
998
1001
|
// overriding them with CLI defaults.
|
|
999
1002
|
if (activeSettings.get("autoResume")) {
|
|
1000
|
-
const manager = await SessionManager.continueRecent(
|
|
1001
|
-
|
|
1003
|
+
const manager = await SessionManager.continueRecent(
|
|
1004
|
+
cwd,
|
|
1005
|
+
sessionDestination(),
|
|
1006
|
+
undefined,
|
|
1007
|
+
migrationPolicy,
|
|
1008
|
+
sessionMemoryMode,
|
|
1009
|
+
);
|
|
1010
|
+
if (manager.hasHistoryEntries()) {
|
|
1002
1011
|
parsed.continue = true;
|
|
1003
1012
|
}
|
|
1004
1013
|
return manager;
|
|
@@ -1720,6 +1729,34 @@ export async function runRootCommand(
|
|
|
1720
1729
|
await postmortem.cleanup();
|
|
1721
1730
|
return;
|
|
1722
1731
|
}
|
|
1732
|
+
// Register a resumed direct session before constructing the agent: GC holds the
|
|
1733
|
+
// same index lock while deleting artifacts, so startup and deletion are fenced.
|
|
1734
|
+
const directSessionId = process.env.GJC_LIFECYCLE_REQUEST_ID ? undefined : sessionManager?.getSessionId();
|
|
1735
|
+
if (directSessionId) {
|
|
1736
|
+
const sessionIndex = new SessionIndex(settingsInstance.getAgentDir());
|
|
1737
|
+
const locator = { repo: sessionManager?.getCwd() ?? cwd, stateRoot: settingsInstance.getAgentDir() };
|
|
1738
|
+
// A pid is reusable, so the broker's teardown fence needs this session's OS
|
|
1739
|
+
// start incarnation recorded alongside the pid it publishes.
|
|
1740
|
+
const directSessionIncarnation = processIncarnation(process.pid);
|
|
1741
|
+
await sessionIndex.append({
|
|
1742
|
+
type: "host_registered",
|
|
1743
|
+
sessionId: directSessionId,
|
|
1744
|
+
locator,
|
|
1745
|
+
endpointGeneration: 0,
|
|
1746
|
+
pid: process.pid,
|
|
1747
|
+
...(directSessionIncarnation ? { processIncarnation: directSessionIncarnation } : {}),
|
|
1748
|
+
});
|
|
1749
|
+
postmortem.register("direct-session-index", async () => {
|
|
1750
|
+
await sessionIndex.append({
|
|
1751
|
+
type: "host_unregistered",
|
|
1752
|
+
sessionId: directSessionId,
|
|
1753
|
+
locator,
|
|
1754
|
+
endpointGeneration: 0,
|
|
1755
|
+
pid: process.pid,
|
|
1756
|
+
});
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1723
1760
|
const createAgentSessionImpl = deps.createAgentSession ?? createAgentSession;
|
|
1724
1761
|
const createSession: CreateSessionForMain = async (options, context): Promise<CreateAgentSessionResult> => {
|
|
1725
1762
|
const result = await logger.time("createAgentSession", createAgentSessionImpl, options);
|
package/src/memories/index.ts
CHANGED
|
@@ -238,7 +238,7 @@ async function runPhase1(options: {
|
|
|
238
238
|
logger.debug("Phase1 skipped: no model available");
|
|
239
239
|
return;
|
|
240
240
|
}
|
|
241
|
-
const phase1ApiKey = await modelRegistry.getApiKey(phase1Model, session.
|
|
241
|
+
const phase1ApiKey = await modelRegistry.getApiKey(phase1Model, session.credentialSessionId);
|
|
242
242
|
if (!phase1ApiKey) {
|
|
243
243
|
logger.debug("Phase1 skipped: no API key for phase1 model", {
|
|
244
244
|
provider: phase1Model.provider,
|
|
@@ -401,7 +401,7 @@ async function runPhase2(options: {
|
|
|
401
401
|
});
|
|
402
402
|
return;
|
|
403
403
|
}
|
|
404
|
-
const phase2ApiKey = await modelRegistry.getApiKey(phase2Model, session.
|
|
404
|
+
const phase2ApiKey = await modelRegistry.getApiKey(phase2Model, session.credentialSessionId);
|
|
405
405
|
if (!phase2ApiKey) {
|
|
406
406
|
markPhase2FailureWithFallback(db, {
|
|
407
407
|
claim,
|
|
@@ -1091,6 +1091,8 @@ async function resolveMemoryModel(options: {
|
|
|
1091
1091
|
settings: session.settings,
|
|
1092
1092
|
matchPreferences: { usageOrder: session.settings.getStorage()?.getModelUsageOrder() },
|
|
1093
1093
|
modelRegistry,
|
|
1094
|
+
sessionId: session.sessionId,
|
|
1095
|
+
credentialSessionId: session.credentialSessionId,
|
|
1094
1096
|
});
|
|
1095
1097
|
if (resolved.model) return resolved.model;
|
|
1096
1098
|
}
|
|
@@ -70,6 +70,7 @@ const ACP_CUSTOM_MODEL_PRESET = "__custom__";
|
|
|
70
70
|
const THINKING_CONFIG_ID = "thinking";
|
|
71
71
|
const SESSION_PAGE_SIZE = 50;
|
|
72
72
|
const MAX_ACP_REPLAY_PAGES = 10_000;
|
|
73
|
+
const MAX_ACP_SESSION_LIST_PAGES = 10_000;
|
|
73
74
|
/** Bounded retention of settled prompt correlations so late duplicates stay closed. */
|
|
74
75
|
const SETTLED_PROMPT_CORRELATION_RETENTION = 16;
|
|
75
76
|
/**
|
|
@@ -108,11 +109,11 @@ interface PromptWaiter {
|
|
|
108
109
|
deferredFrames: JsonObject[];
|
|
109
110
|
/** Activity frames held until acknowledgement establishes exact prompt ownership. */
|
|
110
111
|
deferredActivityFrames: JsonObject[];
|
|
111
|
-
/** Clock reading of the last
|
|
112
|
+
/** Clock reading of the last frame proven to belong to this prompt; watchdog silence baseline. */
|
|
112
113
|
lastFrameAt: number;
|
|
113
|
-
/**
|
|
114
|
+
/** Type of that prompt-owned frame, reported when the watchdog expires. */
|
|
114
115
|
lastFrameType: string;
|
|
115
|
-
/** Cancels the armed inactivity watchdog; re-armed by
|
|
116
|
+
/** Cancels the armed inactivity watchdog; re-armed by prompt-owned frames. */
|
|
116
117
|
cancelWatchdog?: () => void;
|
|
117
118
|
/** What the host is observably doing — a tool running, a model call unanswered — and the bound that follows from it. */
|
|
118
119
|
activity: PromptActivity;
|
|
@@ -139,6 +140,8 @@ type SessionRecord = {
|
|
|
139
140
|
busy: boolean;
|
|
140
141
|
/** Start/update args retained because tool_execution_end does not carry them. */
|
|
141
142
|
toolArgs: Map<string, unknown>;
|
|
143
|
+
/** Message projection state for correlationless session-scoped assistant events. */
|
|
144
|
+
sessionMessageProgress?: { textEmitted: boolean; thoughtEmitted: boolean };
|
|
142
145
|
/** Actionable model-profile authentication failure detected before prompt dispatch. */
|
|
143
146
|
connectionId?: string;
|
|
144
147
|
/** Bounded set of correlations already settled; they stay closed for publication. */
|
|
@@ -148,6 +151,11 @@ type SessionRecord = {
|
|
|
148
151
|
/** Set by `session/cancel` so an in-flight prompt settles as `cancelled`, never as an error. */
|
|
149
152
|
cancelRequested?: boolean;
|
|
150
153
|
};
|
|
154
|
+
|
|
155
|
+
function promptWaiterRetired(record: SessionRecord, waiter: PromptWaiter): boolean {
|
|
156
|
+
return waiter.settled || record.activePrompt !== waiter;
|
|
157
|
+
}
|
|
158
|
+
|
|
151
159
|
type Endpoint = { url: string; token: string };
|
|
152
160
|
|
|
153
161
|
type BrokerSession = {
|
|
@@ -187,6 +195,42 @@ function object(value: unknown): JsonObject | undefined {
|
|
|
187
195
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? (value as JsonObject) : undefined;
|
|
188
196
|
}
|
|
189
197
|
|
|
198
|
+
async function collectAcpSessionList(
|
|
199
|
+
request: (input: JsonObject) => Promise<unknown>,
|
|
200
|
+
input: JsonObject = {},
|
|
201
|
+
): Promise<JsonObject> {
|
|
202
|
+
const aggregate: JsonObject = {};
|
|
203
|
+
const sessions: unknown[] = [];
|
|
204
|
+
let cursor: string | undefined;
|
|
205
|
+
for (let pageCount = 0; pageCount < MAX_ACP_SESSION_LIST_PAGES; pageCount++) {
|
|
206
|
+
const response = object(await request({ ...input, ...(cursor === undefined ? {} : { cursor }) }));
|
|
207
|
+
const listing = object(response?.result) ?? response;
|
|
208
|
+
if (response?.ok === false) {
|
|
209
|
+
const failure = object(response.error);
|
|
210
|
+
throw new AcpSdkAdapterError(
|
|
211
|
+
typeof failure?.code === "string" ? failure.code : "broker_error",
|
|
212
|
+
typeof failure?.message === "string" ? failure.message : "session.list failed",
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (listing) {
|
|
216
|
+
for (const [key, value] of Object.entries(listing)) {
|
|
217
|
+
if (key !== "sessions" && key !== "continuationCursor") aggregate[key] = value;
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(listing.sessions)) sessions.push(...listing.sessions);
|
|
220
|
+
const nextCursor =
|
|
221
|
+
typeof listing.continuationCursor === "string" && listing.continuationCursor.length > 0
|
|
222
|
+
? listing.continuationCursor
|
|
223
|
+
: undefined;
|
|
224
|
+
if (nextCursor) {
|
|
225
|
+
cursor = nextCursor;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { ...aggregate, sessions };
|
|
230
|
+
}
|
|
231
|
+
throw new AcpSdkAdapterError("protocol_error", "session.list exceeded the page budget.");
|
|
232
|
+
}
|
|
233
|
+
|
|
190
234
|
function aggregateAcpFailure(code: string, message: string, failures: unknown[]): AcpSdkAdapterError {
|
|
191
235
|
const aggregate = new AggregateError(failures, message);
|
|
192
236
|
return Object.assign(new AcpSdkAdapterError(code, aggregate.message), {
|
|
@@ -281,25 +325,6 @@ export function acpAvailableCommandsFromSkills(query: unknown): AvailableCommand
|
|
|
281
325
|
return [...commands.values()];
|
|
282
326
|
}
|
|
283
327
|
|
|
284
|
-
function correlationFrom(...values: unknown[]): PromptCorrelation {
|
|
285
|
-
const correlation: PromptCorrelation = {};
|
|
286
|
-
for (const value of values) {
|
|
287
|
-
const candidate = object(value);
|
|
288
|
-
for (const record of [candidate, object(candidate?.result)]) {
|
|
289
|
-
if (!record) continue;
|
|
290
|
-
if (!correlation.commandId) {
|
|
291
|
-
const commandId = record.commandId ?? record.command_id;
|
|
292
|
-
if (typeof commandId === "string" && commandId) correlation.commandId = commandId;
|
|
293
|
-
}
|
|
294
|
-
if (!correlation.turnId) {
|
|
295
|
-
const turnId = record.turnId ?? record.turn_id;
|
|
296
|
-
if (typeof turnId === "string" && turnId) correlation.turnId = turnId;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return correlation;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
328
|
function hasCorrelation(correlation: PromptCorrelation): boolean {
|
|
304
329
|
return correlation.commandId !== undefined || correlation.turnId !== undefined;
|
|
305
330
|
}
|
|
@@ -379,6 +404,36 @@ function strictCorrelationFrom(...values: unknown[]): PromptCorrelation | undefi
|
|
|
379
404
|
return malformed ? undefined : correlation;
|
|
380
405
|
}
|
|
381
406
|
|
|
407
|
+
function sdkFrameCorrelation(frame: JsonObject, event?: JsonObject): PromptCorrelation | undefined {
|
|
408
|
+
return strictCorrelationFrom(frame, event);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Conflicting envelope/event identities own no prompt and therefore cannot
|
|
413
|
+
* refresh a watchdog or publish into either turn.
|
|
414
|
+
*/
|
|
415
|
+
function watchdogCorrelationFrom(frame: JsonObject, event?: JsonObject): PromptCorrelation {
|
|
416
|
+
return strictCorrelationFrom(frame, event) ?? {};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function logDroppedPromptTerminal(
|
|
420
|
+
sessionId: string,
|
|
421
|
+
event: JsonObject,
|
|
422
|
+
reason: "incomplete_correlation" | "correlation_mismatch",
|
|
423
|
+
actual: PromptCorrelation,
|
|
424
|
+
expected?: PromptCorrelation,
|
|
425
|
+
): void {
|
|
426
|
+
logger.error("acp_prompt_terminal_dropped", {
|
|
427
|
+
sessionId,
|
|
428
|
+
terminalType: event.type,
|
|
429
|
+
reason,
|
|
430
|
+
...(actual.commandId ? { commandId: actual.commandId } : {}),
|
|
431
|
+
...(actual.turnId ? { turnId: actual.turnId } : {}),
|
|
432
|
+
...(expected?.commandId ? { expectedCommandId: expected.commandId } : {}),
|
|
433
|
+
...(expected?.turnId ? { expectedTurnId: expected.turnId } : {}),
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
382
437
|
function terminalOutcome(event: JsonObject): SdkPromptTerminalOutcome | undefined {
|
|
383
438
|
const outcome = object(event.outcome);
|
|
384
439
|
if (!outcome) return undefined;
|
|
@@ -1179,9 +1234,10 @@ export class AcpAgent implements Agent {
|
|
|
1179
1234
|
|
|
1180
1235
|
async listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse> {
|
|
1181
1236
|
if (params.cwd) this.#assertAbsoluteCwd(params.cwd);
|
|
1182
|
-
const
|
|
1183
|
-
const listing =
|
|
1184
|
-
const listed = Array.isArray(listing
|
|
1237
|
+
const adapter = await this.#brokerAdapter();
|
|
1238
|
+
const listing = await collectAcpSessionList(input => adapter.global("session.list", input));
|
|
1239
|
+
const listed = Array.isArray(listing.sessions) ? listing.sessions : [];
|
|
1240
|
+
|
|
1185
1241
|
if (params.cwd) {
|
|
1186
1242
|
const discovered = new Set<string>();
|
|
1187
1243
|
for (const session of listed) {
|
|
@@ -1368,6 +1424,10 @@ export class AcpAgent implements Agent {
|
|
|
1368
1424
|
};
|
|
1369
1425
|
record.activePrompt = waiter;
|
|
1370
1426
|
});
|
|
1427
|
+
// The watchdog may reject this waiter while the SDK acknowledgement request is still
|
|
1428
|
+
// pending. Retain the original promise for the caller, but mark that delayed rejection
|
|
1429
|
+
// as observed until prompt() can resume and await it.
|
|
1430
|
+
void response.catch(() => undefined);
|
|
1371
1431
|
// Silence has to be bounded from the moment the prompt owns the session: a host that
|
|
1372
1432
|
// dies before it ever answers is exactly the failure that leaves the client running.
|
|
1373
1433
|
this.#armPromptWatchdog(params.sessionId, record, waiter);
|
|
@@ -1388,7 +1448,7 @@ export class AcpAgent implements Agent {
|
|
|
1388
1448
|
record.adapter,
|
|
1389
1449
|
);
|
|
1390
1450
|
}
|
|
1391
|
-
|
|
1451
|
+
const acknowledgementTask = (async (): Promise<PromptResponse> => {
|
|
1392
1452
|
const acknowledgement = await record.adapter.prompt({
|
|
1393
1453
|
text: payload.text,
|
|
1394
1454
|
...(payload.images.length ? { images: payload.images } : {}),
|
|
@@ -1403,22 +1463,80 @@ export class AcpAgent implements Agent {
|
|
|
1403
1463
|
waiter.boundary = record.inboundSequence;
|
|
1404
1464
|
waiter.correlation = acknowledgementCorrelation;
|
|
1405
1465
|
waiter.acknowledged = true;
|
|
1466
|
+
if (promptWaiterRetired(record, waiter)) {
|
|
1467
|
+
if (
|
|
1468
|
+
!record.settledPromptCorrelations.some(settled =>
|
|
1469
|
+
correlationsExactlyMatch(settled, acknowledgementCorrelation),
|
|
1470
|
+
)
|
|
1471
|
+
) {
|
|
1472
|
+
record.settledPromptCorrelations.push(acknowledgementCorrelation);
|
|
1473
|
+
while (record.settledPromptCorrelations.length > SETTLED_PROMPT_CORRELATION_RETENTION)
|
|
1474
|
+
record.settledPromptCorrelations.shift();
|
|
1475
|
+
}
|
|
1476
|
+
return await response;
|
|
1477
|
+
}
|
|
1406
1478
|
// Frames held while ownership was unknown belong to this prompt only when the
|
|
1407
1479
|
// acknowledgement proves their complete correlation matches exactly.
|
|
1408
1480
|
const deferredActivityFrames = waiter.deferredActivityFrames.splice(0);
|
|
1481
|
+
let observedDeferredActivity = false;
|
|
1409
1482
|
for (const deferredFrame of deferredActivityFrames) {
|
|
1410
1483
|
const deferredEvent = receivedSdkEvent(deferredFrame)?.event;
|
|
1411
|
-
if (correlationsExactlyMatch(waiter.correlation,
|
|
1484
|
+
if (correlationsExactlyMatch(waiter.correlation, watchdogCorrelationFrom(deferredFrame, deferredEvent))) {
|
|
1412
1485
|
this.#observePromptActivity(waiter, deferredFrame);
|
|
1486
|
+
observedDeferredActivity = true;
|
|
1487
|
+
}
|
|
1413
1488
|
}
|
|
1414
|
-
if (
|
|
1489
|
+
if (observedDeferredActivity) this.#armPromptWatchdog(params.sessionId, record, waiter);
|
|
1415
1490
|
const deferred = waiter.deferredFrames.splice(0);
|
|
1416
|
-
for (const deferredFrame of deferred)
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1491
|
+
for (const deferredFrame of deferred) {
|
|
1492
|
+
const deferredEvent = receivedSdkEvent(deferredFrame)?.event;
|
|
1493
|
+
if (!deferredEvent) continue;
|
|
1494
|
+
const deferredCorrelation = sdkFrameCorrelation(deferredFrame, deferredEvent) ?? {};
|
|
1495
|
+
const deferredIsTerminal = deferredEvent.type === "agent_end" || deferredEvent.type === "agent_failed";
|
|
1496
|
+
if (!hasCompleteCorrelation(deferredCorrelation)) {
|
|
1497
|
+
if (deferredIsTerminal)
|
|
1498
|
+
logDroppedPromptTerminal(
|
|
1499
|
+
params.sessionId,
|
|
1500
|
+
deferredEvent,
|
|
1501
|
+
"incomplete_correlation",
|
|
1502
|
+
deferredCorrelation,
|
|
1503
|
+
waiter.correlation,
|
|
1504
|
+
);
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
const matchesPrompt = correlationsExactlyMatch(waiter.correlation, deferredCorrelation);
|
|
1508
|
+
if (
|
|
1509
|
+
!matchesPrompt &&
|
|
1510
|
+
record.settledPromptCorrelations.some(settled => correlationsExactlyMatch(settled, deferredCorrelation))
|
|
1511
|
+
)
|
|
1512
|
+
continue;
|
|
1513
|
+
if (!matchesPrompt) {
|
|
1514
|
+
if (deferredIsTerminal)
|
|
1515
|
+
logDroppedPromptTerminal(
|
|
1516
|
+
params.sessionId,
|
|
1517
|
+
deferredEvent,
|
|
1518
|
+
"correlation_mismatch",
|
|
1519
|
+
deferredCorrelation,
|
|
1520
|
+
waiter.correlation,
|
|
1521
|
+
);
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
const task = record.frameTail.then(
|
|
1525
|
+
async () => await this.#handleSdkFrame(params.sessionId, record.adapter, deferredFrame),
|
|
1526
|
+
);
|
|
1527
|
+
record.frameTail = task.catch(
|
|
1528
|
+
async error =>
|
|
1529
|
+
await this.#failSession(params.sessionId, record.adapter, this.#frameProcessingFailure(error)),
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1421
1532
|
this.#settlePrompt(record, waiter);
|
|
1533
|
+
return await response;
|
|
1534
|
+
})();
|
|
1535
|
+
// Settlement can win before the SDK answers `turn.prompt`; acknowledgement processing
|
|
1536
|
+
// must remain alive to tombstone its eventual correlation without holding the ACP caller.
|
|
1537
|
+
void acknowledgementTask.catch(() => undefined);
|
|
1538
|
+
try {
|
|
1539
|
+
return await Promise.race([response, acknowledgementTask]);
|
|
1422
1540
|
} catch (error) {
|
|
1423
1541
|
waiter.deferredFrames.length = 0;
|
|
1424
1542
|
waiter.deferredActivityFrames.length = 0;
|
|
@@ -1437,7 +1555,6 @@ export class AcpAgent implements Agent {
|
|
|
1437
1555
|
}
|
|
1438
1556
|
throw error;
|
|
1439
1557
|
}
|
|
1440
|
-
return await response;
|
|
1441
1558
|
}
|
|
1442
1559
|
|
|
1443
1560
|
async cancel(params: { sessionId: string }): Promise<void> {
|
|
@@ -1655,8 +1772,9 @@ export class AcpAgent implements Agent {
|
|
|
1655
1772
|
}
|
|
1656
1773
|
|
|
1657
1774
|
async #scopedBrokerSession(id: string, cwd: string): Promise<BrokerSession | undefined> {
|
|
1658
|
-
const
|
|
1659
|
-
const result =
|
|
1775
|
+
const adapter = await this.#brokerAdapter();
|
|
1776
|
+
const result = await collectAcpSessionList(input => adapter.global("session.list", input), { cwd });
|
|
1777
|
+
|
|
1660
1778
|
const matches: BrokerSession[] = [];
|
|
1661
1779
|
for (const item of Array.isArray(result?.sessions) ? result.sessions : []) {
|
|
1662
1780
|
const session = object(item) as BrokerSession | undefined;
|
|
@@ -1926,10 +2044,11 @@ export class AcpAgent implements Agent {
|
|
|
1926
2044
|
}
|
|
1927
2045
|
|
|
1928
2046
|
async #resolveSavedSession(id: string, cwd: string): Promise<string> {
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2047
|
+
const adapter = await this.#brokerAdapter();
|
|
2048
|
+
const result = await collectAcpSessionList(input => adapter.global("session.list", input), {
|
|
2049
|
+
resolveSessionId: id,
|
|
2050
|
+
cwd,
|
|
2051
|
+
});
|
|
1933
2052
|
const saved = object(result?.savedSession);
|
|
1934
2053
|
if (saved?.id !== id || typeof saved.path !== "string")
|
|
1935
2054
|
throw new AcpSdkAdapterError("not_found", `Saved ACP session does not exist: ${id}`);
|
|
@@ -1979,38 +2098,33 @@ export class AcpAgent implements Agent {
|
|
|
1979
2098
|
}
|
|
1980
2099
|
|
|
1981
2100
|
/**
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
2101
|
+
* A frame only restarts this prompt's watchdog after its complete correlation proves
|
|
2102
|
+
* that it belongs to the prompt. Correlationless or foreign traffic still proves the
|
|
2103
|
+
* session host process is alive, but not that it is making progress on this turn; using
|
|
2104
|
+
* process liveness as turn liveness lets an otherwise healthy host keep a wedged prompt
|
|
2105
|
+
* open forever. Matching frames retain the per-gap behavior for hosts demonstrably
|
|
2106
|
+
* working on this turn, while pre-acknowledgement activity is replayed after ownership
|
|
2107
|
+
* becomes known.
|
|
1986
2108
|
*/
|
|
1987
2109
|
#refreshPromptWatchdog(id: string, record: SessionRecord, frame: JsonObject): void {
|
|
1988
2110
|
const waiter = record.activePrompt;
|
|
1989
2111
|
if (!waiter || waiter.settled) return;
|
|
1990
2112
|
const event = receivedSdkEvent(frame)?.event;
|
|
1991
|
-
|
|
1992
|
-
waiter.lastFrameType =
|
|
1993
|
-
typeof event?.type === "string" ? event.type : typeof frame.type === "string" ? frame.type : "unknown";
|
|
1994
|
-
// Which bound applies is per-turn state, so it is fenced by the same identity that gates
|
|
1995
|
-
// settlement: only a frame carrying exactly the acknowledged prompt's command/turn may
|
|
1996
|
-
// start, clear, or extend a bound. Without this fence an already-settled turn's flushed
|
|
1997
|
-
// frames mutate the live turn — a stale `message_update` clears `awaitingModel` and
|
|
1998
|
-
// narrows it off the inference bound, a stale `tool_execution_start` pins it to the tool
|
|
1999
|
-
// bound with no tool running. Frames with no correlation at all (heartbeats,
|
|
2000
|
-
// session-scoped events) are refresh-only for the same reason: they prove the producer
|
|
2001
|
-
// lives but say nothing about what this turn is doing, so they leave the bound as is.
|
|
2002
|
-
// Before acknowledgement the prompt has no identity, so correlated activity is retained
|
|
2003
|
-
// and folded in only after the acknowledgement proves exact ownership.
|
|
2113
|
+
const correlation = watchdogCorrelationFrom(frame, event);
|
|
2004
2114
|
if (!waiter.acknowledged) {
|
|
2005
|
-
if (hasCorrelation(
|
|
2006
|
-
|
|
2007
|
-
this.#observePromptActivity(waiter, frame);
|
|
2115
|
+
if (hasCorrelation(correlation)) waiter.deferredActivityFrames.push(frame);
|
|
2116
|
+
return;
|
|
2008
2117
|
}
|
|
2118
|
+
if (!correlationsExactlyMatch(waiter.correlation, correlation)) return;
|
|
2119
|
+
this.#observePromptActivity(waiter, frame);
|
|
2009
2120
|
this.#armPromptWatchdog(id, record, waiter);
|
|
2010
2121
|
}
|
|
2011
2122
|
|
|
2012
2123
|
#observePromptActivity(waiter: PromptWaiter, frame: JsonObject): void {
|
|
2013
2124
|
const event = receivedSdkEvent(frame)?.event;
|
|
2125
|
+
waiter.lastFrameAt = this.#promptWatchdogClock.now();
|
|
2126
|
+
waiter.lastFrameType =
|
|
2127
|
+
typeof event?.type === "string" ? event.type : typeof frame.type === "string" ? frame.type : "unknown";
|
|
2014
2128
|
waiter.activity.observe(
|
|
2015
2129
|
typeof event?.type === "string" ? event.type : undefined,
|
|
2016
2130
|
typeof event?.toolCallId === "string" ? event.toolCallId : undefined,
|
|
@@ -2062,8 +2176,8 @@ export class AcpAgent implements Agent {
|
|
|
2062
2176
|
if (!record || record.adapter !== adapter) return;
|
|
2063
2177
|
// Ingress ordering is recorded before queued work begins.
|
|
2064
2178
|
this.#observeSessionActivity(record, frame);
|
|
2065
|
-
//
|
|
2066
|
-
//
|
|
2179
|
+
// Correlation is checked at ingress before a prompt-owned frame may refresh the
|
|
2180
|
+
// watchdog, so queued processing cannot turn unrelated host traffic into turn liveness.
|
|
2067
2181
|
this.#refreshPromptWatchdog(id, record, frame);
|
|
2068
2182
|
++record.inboundSequence;
|
|
2069
2183
|
const task = record.frameTail.then(async () => await this.#handleSdkFrame(id, adapter, frame));
|
|
@@ -2103,19 +2217,33 @@ export class AcpAgent implements Agent {
|
|
|
2103
2217
|
if (!received) return;
|
|
2104
2218
|
const { event, wirePayload } = received;
|
|
2105
2219
|
const isTerminal = event.type === "agent_end" || event.type === "agent_failed";
|
|
2106
|
-
const
|
|
2220
|
+
const derivedCorrelation = sdkFrameCorrelation(frame, event);
|
|
2221
|
+
const correlation = derivedCorrelation ?? {};
|
|
2107
2222
|
const activePrompt = record.activePrompt;
|
|
2108
2223
|
const outcome = isTerminal ? terminalOutcome(event) : undefined;
|
|
2224
|
+
const settledCorrelation = record.settledPromptCorrelations.some(settled =>
|
|
2225
|
+
correlationsMatch(settled, correlation),
|
|
2226
|
+
);
|
|
2109
2227
|
if (isTerminal) {
|
|
2110
2228
|
// Terminal ownership requires a complete identity. Unowned, partial, and
|
|
2111
2229
|
// duplicate terminals are never allowed to publish or query anything.
|
|
2112
|
-
if (!
|
|
2230
|
+
if (!activePrompt || activePrompt.settled) return;
|
|
2231
|
+
if (!hasCompleteCorrelation(correlation)) {
|
|
2232
|
+
logDroppedPromptTerminal(id, event, "incomplete_correlation", correlation, activePrompt.correlation);
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2113
2235
|
if (!activePrompt.acknowledged) {
|
|
2114
2236
|
// Hold the entire frame until the prompt acknowledgement proves ownership.
|
|
2115
2237
|
activePrompt.deferredFrames.push(frame);
|
|
2116
2238
|
return;
|
|
2117
2239
|
}
|
|
2118
|
-
|
|
2240
|
+
const matchesPrompt = correlationsExactlyMatch(activePrompt.correlation, correlation);
|
|
2241
|
+
if (settledCorrelation && !matchesPrompt) return;
|
|
2242
|
+
if (!matchesPrompt) {
|
|
2243
|
+
logDroppedPromptTerminal(id, event, "correlation_mismatch", correlation, activePrompt.correlation);
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
if (activePrompt.terminal) return;
|
|
2119
2247
|
if (!outcome) {
|
|
2120
2248
|
const detail =
|
|
2121
2249
|
typeof (event as { error?: { message?: unknown } }).error?.message === "string"
|
|
@@ -2131,17 +2259,15 @@ export class AcpAgent implements Agent {
|
|
|
2131
2259
|
}
|
|
2132
2260
|
activePrompt.terminal = { outcome, correlation };
|
|
2133
2261
|
}
|
|
2134
|
-
|
|
2135
|
-
if (
|
|
2136
|
-
|
|
2137
|
-
(
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2262
|
+
if (!isTerminal && derivedCorrelation === undefined) return;
|
|
2263
|
+
if (!isTerminal && hasCorrelation(correlation)) {
|
|
2264
|
+
if (!activePrompt || activePrompt.settled) return;
|
|
2265
|
+
if (!activePrompt.acknowledged) {
|
|
2266
|
+
activePrompt.deferredFrames.push(frame);
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
if (!correlationsExactlyMatch(activePrompt.correlation, correlation)) return;
|
|
2141
2270
|
}
|
|
2142
|
-
const settledCorrelation = record.settledPromptCorrelations.some(settled =>
|
|
2143
|
-
correlationsMatch(settled, correlation),
|
|
2144
|
-
);
|
|
2145
2271
|
if (settledCorrelation) {
|
|
2146
2272
|
// Frames for an already-settled correlation stay closed until an active prompt
|
|
2147
2273
|
// acknowledges the exact same identity.
|
|
@@ -2151,25 +2277,41 @@ export class AcpAgent implements Agent {
|
|
|
2151
2277
|
}
|
|
2152
2278
|
if (!activePrompt || activePrompt.settled || !correlationsMatch(activePrompt.correlation, correlation)) return;
|
|
2153
2279
|
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2280
|
+
const promptOwner =
|
|
2281
|
+
activePrompt &&
|
|
2282
|
+
!activePrompt.settled &&
|
|
2283
|
+
activePrompt.acknowledged &&
|
|
2284
|
+
correlationsExactlyMatch(activePrompt.correlation, correlation)
|
|
2285
|
+
? activePrompt
|
|
2286
|
+
: undefined;
|
|
2287
|
+
const toolCallId = typeof event.toolCallId === "string" ? event.toolCallId : undefined;
|
|
2288
|
+
if (
|
|
2289
|
+
toolCallId &&
|
|
2290
|
+
(event.type === "tool_execution_start" || event.type === "tool_execution_update") &&
|
|
2291
|
+
"args" in event
|
|
2292
|
+
) {
|
|
2293
|
+
record.toolArgs.set(toolCallId, event.args);
|
|
2294
|
+
}
|
|
2157
2295
|
if (wirePayload) {
|
|
2158
2296
|
for (const notification of mapAgentWireEventPayloadToAcpSessionUpdates(wirePayload as never, id, {
|
|
2159
2297
|
cwd: record.cwd,
|
|
2160
2298
|
getToolArgs: id => record.toolArgs.get(id),
|
|
2161
2299
|
getMessageProgress: message => {
|
|
2162
|
-
if (!
|
|
2163
|
-
|
|
2164
|
-
|
|
2300
|
+
if (!object(message)) return undefined;
|
|
2301
|
+
if (promptOwner) {
|
|
2302
|
+
promptOwner.messageProgress ??= { textEmitted: false, thoughtEmitted: false };
|
|
2303
|
+
return promptOwner.messageProgress;
|
|
2304
|
+
}
|
|
2305
|
+
record.sessionMessageProgress ??= { textEmitted: false, thoughtEmitted: false };
|
|
2306
|
+
return record.sessionMessageProgress;
|
|
2165
2307
|
},
|
|
2166
2308
|
})) {
|
|
2167
2309
|
if (
|
|
2168
|
-
|
|
2310
|
+
promptOwner &&
|
|
2169
2311
|
notification.update.sessionUpdate === "agent_message_chunk" &&
|
|
2170
2312
|
notification.update.content.type === "text"
|
|
2171
2313
|
)
|
|
2172
|
-
|
|
2314
|
+
promptOwner.emittedAssistantText += notification.update.content.text;
|
|
2173
2315
|
await this.#publishSessionUpdate(id, notification, adapter);
|
|
2174
2316
|
}
|
|
2175
2317
|
}
|
|
@@ -2188,14 +2330,16 @@ export class AcpAgent implements Agent {
|
|
|
2188
2330
|
adapter,
|
|
2189
2331
|
);
|
|
2190
2332
|
}
|
|
2191
|
-
if (event.type === "message_end" && object(event.message)?.role === "assistant"
|
|
2192
|
-
|
|
2333
|
+
if (event.type === "message_end" && object(event.message)?.role === "assistant") {
|
|
2334
|
+
if (promptOwner) promptOwner.messageProgress = undefined;
|
|
2335
|
+
else record.sessionMessageProgress = undefined;
|
|
2336
|
+
}
|
|
2193
2337
|
if (event.type === "agent_end") {
|
|
2194
2338
|
const finalText = typeof event.finalText === "string" ? event.finalText : "";
|
|
2195
|
-
if (
|
|
2196
|
-
const resolution = resolveAcpFinalText(
|
|
2339
|
+
if (promptOwner && finalText) {
|
|
2340
|
+
const resolution = resolveAcpFinalText(promptOwner.emittedAssistantText, finalText);
|
|
2197
2341
|
if (resolution.kind === "emit") {
|
|
2198
|
-
|
|
2342
|
+
promptOwner.emittedAssistantText += resolution.text;
|
|
2199
2343
|
await this.#publishSessionUpdate(
|
|
2200
2344
|
id,
|
|
2201
2345
|
{
|
|
@@ -2211,9 +2355,9 @@ export class AcpAgent implements Agent {
|
|
|
2211
2355
|
} else if (resolution.kind === "divergent") {
|
|
2212
2356
|
logger.warn("acp_final_text_diverged", {
|
|
2213
2357
|
sessionId: id,
|
|
2214
|
-
...(
|
|
2215
|
-
...(
|
|
2216
|
-
streamedLength:
|
|
2358
|
+
...(promptOwner.correlation.commandId ? { commandId: promptOwner.correlation.commandId } : {}),
|
|
2359
|
+
...(promptOwner.correlation.turnId ? { turnId: promptOwner.correlation.turnId } : {}),
|
|
2360
|
+
streamedLength: promptOwner.emittedAssistantText.length,
|
|
2217
2361
|
finalLength: resolution.final.text.length,
|
|
2218
2362
|
});
|
|
2219
2363
|
}
|