@gajae-code/coding-agent 0.11.10 → 0.11.11
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 +38 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-profile-contract.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +57 -2
- package/dist/types/config/settings.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +60 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/modes/acp/acp-agent.d.ts +14 -9
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/manager.d.ts +8 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +3 -1
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +23 -3
- package/dist/types/sdk/host/control/operations.d.ts +1 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/startup-capability.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +9 -11
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +27 -0
- package/dist/types/session/session-manager.d.ts +34 -0
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/types.d.ts +36 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/resource-gc.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/src/cli/args.ts +14 -8
- package/src/cli/fast-help.ts +1 -1
- package/src/cli.ts +48 -2
- package/src/commands/sdk.ts +91 -7
- package/src/config/model-profile-activation.ts +1 -0
- package/src/config/model-profile-contract.ts +13 -0
- package/src/config/model-profiles.ts +24 -0
- package/src/config/settings-schema.ts +61 -2
- package/src/config/settings.ts +8 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +14 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +16 -2
- package/src/gjc-runtime/deep-interview-state.ts +25 -3
- package/src/gjc-runtime/linux-proc.ts +70 -36
- package/src/gjc-runtime/ralplan-runtime.ts +252 -1
- package/src/gjc-runtime/state-runtime.ts +48 -11
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/internal-urls/local-protocol.ts +23 -4
- package/src/modes/acp/acp-agent.ts +554 -60
- package/src/modes/acp/acp-event-mapper.ts +93 -6
- package/src/modes/components/model-selector.ts +15 -1
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/tool-status-header.ts +1 -4
- package/src/modes/components/welcome.ts +18 -3
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +44 -3
- package/src/modes/interactive-mode.ts +65 -26
- package/src/modes/runtime-init.ts +1 -0
- package/src/prompts/agents/executor.md +1 -1
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/manager.ts +26 -6
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/lifecycle.ts +82 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/config.ts +1 -1
- package/src/sdk/bus/index.ts +162 -22
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -4
- package/src/sdk/bus/telegram-daemon.ts +531 -53
- package/src/sdk/host/control/dispatch.ts +2 -0
- package/src/sdk/host/control/operations.ts +1 -0
- package/src/sdk/host/host.ts +2 -2
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/protocol/operation-inventory.generated.json +47 -0
- package/src/sdk/protocol/operation-registry.ts +1 -0
- package/src/sdk/session.ts +12 -1
- package/src/sdk/startup-capability.ts +6 -0
- package/src/session/agent-session.ts +256 -142
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +40 -10
- package/src/session/internal/native-publish-outcome.ts +8 -3
- package/src/session/session-manager.ts +608 -25
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +2 -0
- package/src/task/types.ts +22 -11
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/resource-gc.ts +496 -120
- package/src/workflow/workflow-intent-diff.ts +4 -1
package/src/sdk/bus/index.ts
CHANGED
|
@@ -31,7 +31,8 @@ import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
|
31
31
|
import type { ImageContent, TextContent, Tool } from "@gajae-code/ai";
|
|
32
32
|
import { NotificationServer, nativeBuildInfo } from "@gajae-code/natives";
|
|
33
33
|
import { logger, postmortem, VERSION } from "@gajae-code/utils";
|
|
34
|
-
import { projectModelProfileCatalog } from "../../config/model-profile-contract";
|
|
34
|
+
import { isModelProfileProviderAvailable, projectModelProfileCatalog } from "../../config/model-profile-contract";
|
|
35
|
+
import { isAuthenticated, kNoAuth } from "../../config/model-registry";
|
|
35
36
|
import { Settings } from "../../config/settings";
|
|
36
37
|
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "../../extensibility/extensions";
|
|
37
38
|
import { toAgentWireEventPayload } from "../../modes/shared/agent-wire/event-envelope";
|
|
@@ -54,6 +55,7 @@ import type {
|
|
|
54
55
|
AskSettlementResult,
|
|
55
56
|
} from "../../tools";
|
|
56
57
|
import { registerAskAnswerSource, registerWorkflowGateEmitterListener } from "../../tools/ask-answer-registry";
|
|
58
|
+
import { acpFinalTextFromMessage } from "../acp/final-text";
|
|
57
59
|
import { ensureBroker } from "../broker/ensure";
|
|
58
60
|
import { SessionIndex } from "../broker/session-index";
|
|
59
61
|
import { SessionSdkHost, shouldHostSdk } from "../host";
|
|
@@ -935,7 +937,13 @@ interface SessionRuntime {
|
|
|
935
937
|
emitPromptLifecycle: (
|
|
936
938
|
correlation: { commandId: string; turnId: string } | undefined,
|
|
937
939
|
frame:
|
|
938
|
-
| {
|
|
940
|
+
| {
|
|
941
|
+
type: "agent_start" | "agent_end";
|
|
942
|
+
sessionId: string;
|
|
943
|
+
commandId?: string;
|
|
944
|
+
turnId?: string;
|
|
945
|
+
finalText?: string;
|
|
946
|
+
}
|
|
939
947
|
| {
|
|
940
948
|
type: "agent_failed";
|
|
941
949
|
sessionId: string;
|
|
@@ -975,7 +983,10 @@ interface SessionRuntime {
|
|
|
975
983
|
/** SDK control frames received during provisional ownership; replayed only after stable activation. */
|
|
976
984
|
deferredInboundControls: Array<() => void>;
|
|
977
985
|
/** Started tool calls awaiting a terminal activity frame, keyed by tool call id. */
|
|
978
|
-
inFlightTools: Map<
|
|
986
|
+
inFlightTools: Map<
|
|
987
|
+
string,
|
|
988
|
+
{ toolName: string; args?: unknown; pendingPhase?: "completed" | "failed" | "cancelled" }
|
|
989
|
+
>;
|
|
979
990
|
/** Cancels the postmortem cleanup that emits `session_closed` on process teardown. */
|
|
980
991
|
cancelPostmortemCleanup: () => void;
|
|
981
992
|
/** Disposes side-turn resources when their owning logical session becomes unavailable. */
|
|
@@ -1105,7 +1116,7 @@ const defaultConfig: NotificationConfig = {
|
|
|
1105
1116
|
idleTimeoutMs: 60_000,
|
|
1106
1117
|
rich: { enabled: true },
|
|
1107
1118
|
richDraft: { enabled: false },
|
|
1108
|
-
toolActivity: { enabled:
|
|
1119
|
+
toolActivity: { enabled: false },
|
|
1109
1120
|
streaming: { enabled: true },
|
|
1110
1121
|
topics: {},
|
|
1111
1122
|
btw: { enabled: true },
|
|
@@ -1698,6 +1709,7 @@ const UNINSTALLED_CONTROL_OPERATIONS = new Set(["auth.login", "host_tools.regist
|
|
|
1698
1709
|
|
|
1699
1710
|
const CONTROL_BINDINGS: Readonly<Record<string, string | undefined>> = {
|
|
1700
1711
|
"model.cycle": "cycleModel",
|
|
1712
|
+
"model.profile.set": "setModelProfile",
|
|
1701
1713
|
"thinking.cycle": "cycleThinkingLevel",
|
|
1702
1714
|
"queue.steering_mode.set": "setQueueMode",
|
|
1703
1715
|
"queue.follow_up_mode.set": "setQueueMode",
|
|
@@ -1862,8 +1874,26 @@ function sdkQuerySurface(
|
|
|
1862
1874
|
const currentThinkingLevel = api.getThinkingLevel();
|
|
1863
1875
|
return projectQ10Models({ models, currentModel, currentThinkingLevel });
|
|
1864
1876
|
},
|
|
1865
|
-
getModelProfiles: () =>
|
|
1866
|
-
|
|
1877
|
+
getModelProfiles: async () => {
|
|
1878
|
+
const profiles = ctx.modelRegistry.getModelProfiles();
|
|
1879
|
+
const catalog = projectModelProfileCatalog(profiles, ctx.modelRegistry.getError());
|
|
1880
|
+
const providers = new Set([...profiles.values()].flatMap(profile => profile.requiredProviders));
|
|
1881
|
+
const authenticatedProviders = new Set<string>();
|
|
1882
|
+
await Promise.all(
|
|
1883
|
+
[...providers].map(async provider => {
|
|
1884
|
+
try {
|
|
1885
|
+
const credential = await ctx.modelRegistry.getApiKeyForProvider(provider, id);
|
|
1886
|
+
if (credential === kNoAuth || isAuthenticated(credential)) authenticatedProviders.add(provider);
|
|
1887
|
+
} catch {
|
|
1888
|
+
// A provider whose credential state cannot be read is not currently configurable.
|
|
1889
|
+
}
|
|
1890
|
+
}),
|
|
1891
|
+
);
|
|
1892
|
+
return catalog.map(item => ({
|
|
1893
|
+
...item,
|
|
1894
|
+
available: isModelProfileProviderAvailable(profiles.get(item.id)!, authenticatedProviders),
|
|
1895
|
+
}));
|
|
1896
|
+
},
|
|
1867
1897
|
getSkillState: () => ctx.getSkillState(),
|
|
1868
1898
|
getGates: () => {
|
|
1869
1899
|
const workflowGate = ctx.workflowGate;
|
|
@@ -2333,6 +2363,14 @@ function sdkControlSurface(
|
|
|
2333
2363
|
);
|
|
2334
2364
|
return typed("model.set", { id: `${model.provider}/${model.id}`, thinkingLevel });
|
|
2335
2365
|
},
|
|
2366
|
+
setModelProfile: async id => {
|
|
2367
|
+
if (!bindings.has("setModelProfile") || !ctx.setModelProfile)
|
|
2368
|
+
return unavailable("model.profile.set", "no model-profile activation seam is installed")();
|
|
2369
|
+
if (!id) throw Object.assign(new Error("model.profile.set requires a profile id."), { code: "invalid_input" });
|
|
2370
|
+
if (!ctx.modelRegistry.getModelProfile(id))
|
|
2371
|
+
throw Object.assign(new Error(`Model profile ${id} was not found.`), { code: "invalid_input" });
|
|
2372
|
+
return { changed: await ctx.setModelProfile(id), id };
|
|
2373
|
+
},
|
|
2336
2374
|
cycleModel: async () => {
|
|
2337
2375
|
if (!bindings.has("cycleModel"))
|
|
2338
2376
|
return unavailable("model.cycle", "no session model-cycle seam is installed")();
|
|
@@ -3219,7 +3257,13 @@ export function createNotificationsExtension(
|
|
|
3219
3257
|
const promptSubmissionKey = (correlation: { commandId: string; turnId: string }) =>
|
|
3220
3258
|
`${correlation.commandId}:${correlation.turnId}`;
|
|
3221
3259
|
type PromptLifecycleFrame =
|
|
3222
|
-
| {
|
|
3260
|
+
| {
|
|
3261
|
+
type: "agent_start" | "agent_end";
|
|
3262
|
+
sessionId: string;
|
|
3263
|
+
commandId?: string;
|
|
3264
|
+
turnId?: string;
|
|
3265
|
+
finalText?: string;
|
|
3266
|
+
}
|
|
3223
3267
|
| {
|
|
3224
3268
|
type: "agent_failed";
|
|
3225
3269
|
sessionId: string;
|
|
@@ -3669,7 +3713,10 @@ export function createNotificationsExtension(
|
|
|
3669
3713
|
emitPromptLifecycle,
|
|
3670
3714
|
emitPromptEvent,
|
|
3671
3715
|
pendingInbound: new Set<number>(),
|
|
3672
|
-
inFlightTools: new Map<
|
|
3716
|
+
inFlightTools: new Map<
|
|
3717
|
+
string,
|
|
3718
|
+
{ toolName: string; args?: unknown; pendingPhase?: "completed" | "failed" | "cancelled" }
|
|
3719
|
+
>(),
|
|
3673
3720
|
deferredGatePresentations: [],
|
|
3674
3721
|
deferredInboundControls: [],
|
|
3675
3722
|
notificationRootRegistration: undefined,
|
|
@@ -4103,7 +4150,7 @@ export function createNotificationsExtension(
|
|
|
4103
4150
|
}
|
|
4104
4151
|
if (typeof inbound.redact === "boolean") {
|
|
4105
4152
|
if (inbound.redact && !runtime.committedRedact) {
|
|
4106
|
-
terminalizeInFlightTools(runtime, runtime.id, "
|
|
4153
|
+
terminalizeInFlightTools(runtime, runtime.id, "cancelled");
|
|
4107
4154
|
}
|
|
4108
4155
|
runtime.committedRedact = inbound.redact;
|
|
4109
4156
|
runtime.redact = inbound.redact;
|
|
@@ -4450,8 +4497,15 @@ export function createNotificationsExtension(
|
|
|
4450
4497
|
runtime.redact = true;
|
|
4451
4498
|
runtime.verbosity = "lean";
|
|
4452
4499
|
runtime.stream = false;
|
|
4500
|
+
for (const [toolCallId, tool] of runtime.inFlightTools) {
|
|
4501
|
+
runtime.inFlightTools.set(toolCallId, {
|
|
4502
|
+
toolName: tool.toolName,
|
|
4503
|
+
...(tool.pendingPhase ? { pendingPhase: tool.pendingPhase } : {}),
|
|
4504
|
+
});
|
|
4505
|
+
}
|
|
4453
4506
|
return;
|
|
4454
4507
|
}
|
|
4508
|
+
const wasPolicySuspended = runtime.policySuspended;
|
|
4455
4509
|
const redactionEnabled = policy.redact && !runtime.committedRedact;
|
|
4456
4510
|
runtime.policyGeneration++;
|
|
4457
4511
|
runtime.committedRedact = policy.redact;
|
|
@@ -4459,7 +4513,8 @@ export function createNotificationsExtension(
|
|
|
4459
4513
|
runtime.redact = policy.redact;
|
|
4460
4514
|
runtime.verbosity = policy.verbosity;
|
|
4461
4515
|
runtime.stream = policy.stream;
|
|
4462
|
-
if (redactionEnabled) terminalizeInFlightTools(runtime, runtime.id, "
|
|
4516
|
+
if (redactionEnabled) terminalizeInFlightTools(runtime, runtime.id, "cancelled", true);
|
|
4517
|
+
else if (wasPolicySuspended && !policy.redact) settleProvisionalToolTerminals(runtime, runtime.id);
|
|
4463
4518
|
},
|
|
4464
4519
|
activate: binding => {
|
|
4465
4520
|
const runtime = runtimes.get(binding.sessionId);
|
|
@@ -4702,8 +4757,22 @@ export function createNotificationsExtension(
|
|
|
4702
4757
|
await rotateSessionAuthority(event, ctx, false);
|
|
4703
4758
|
});
|
|
4704
4759
|
|
|
4705
|
-
const terminalizeInFlightTools = (
|
|
4706
|
-
|
|
4760
|
+
const terminalizeInFlightTools = (
|
|
4761
|
+
rt: SessionRuntime,
|
|
4762
|
+
id: string,
|
|
4763
|
+
phase: "cancelled" | "failed",
|
|
4764
|
+
allowSafeRedactedFrame = false,
|
|
4765
|
+
): void => {
|
|
4766
|
+
if (rt.policySuspended && !allowSafeRedactedFrame) {
|
|
4767
|
+
for (const [toolCallId, tool] of rt.inFlightTools) {
|
|
4768
|
+
rt.inFlightTools.set(toolCallId, {
|
|
4769
|
+
toolName: tool.toolName,
|
|
4770
|
+
pendingPhase: tool.pendingPhase ?? phase,
|
|
4771
|
+
});
|
|
4772
|
+
}
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
if (rt.notificationsActive && (!rt.redact || allowSafeRedactedFrame)) {
|
|
4707
4776
|
for (const [toolCallId, { toolName }] of rt.inFlightTools) {
|
|
4708
4777
|
try {
|
|
4709
4778
|
pushSessionFrame(rt, { type: "tool_activity", sessionId: id, toolCallId, toolName, phase });
|
|
@@ -4715,6 +4784,27 @@ export function createNotificationsExtension(
|
|
|
4715
4784
|
rt.inFlightTools.clear();
|
|
4716
4785
|
};
|
|
4717
4786
|
|
|
4787
|
+
const settleProvisionalToolTerminals = (rt: SessionRuntime, id: string): void => {
|
|
4788
|
+
for (const [toolCallId, tool] of rt.inFlightTools) {
|
|
4789
|
+
if (!tool.pendingPhase) continue;
|
|
4790
|
+
try {
|
|
4791
|
+
if (rt.notificationsActive && !rt.redact) {
|
|
4792
|
+
pushSessionFrame(rt, {
|
|
4793
|
+
type: "tool_activity",
|
|
4794
|
+
sessionId: id,
|
|
4795
|
+
toolCallId,
|
|
4796
|
+
toolName: tool.toolName,
|
|
4797
|
+
phase: tool.pendingPhase,
|
|
4798
|
+
});
|
|
4799
|
+
}
|
|
4800
|
+
} catch (e) {
|
|
4801
|
+
logger.warn(`notifications: provisional tool_activity settlement failed: ${String(e)}`);
|
|
4802
|
+
} finally {
|
|
4803
|
+
rt.inFlightTools.delete(toolCallId);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
};
|
|
4807
|
+
|
|
4718
4808
|
const resetTurnStreamState = (rt: SessionRuntime): void => {
|
|
4719
4809
|
rt.currentTurnText = undefined;
|
|
4720
4810
|
rt.preAskFlushedText = undefined;
|
|
@@ -4851,14 +4941,28 @@ export function createNotificationsExtension(
|
|
|
4851
4941
|
});
|
|
4852
4942
|
else {
|
|
4853
4943
|
rt.notePromptReconciliation(correlation, { type: "agent_end" });
|
|
4854
|
-
if (rt.recordPromptTerminal(correlation))
|
|
4855
|
-
|
|
4944
|
+
if (rt.recordPromptTerminal(correlation)) {
|
|
4945
|
+
const finalAssistant = (Array.isArray(event.messages) ? [...event.messages].reverse() : []).find(
|
|
4946
|
+
message =>
|
|
4947
|
+
message &&
|
|
4948
|
+
typeof message === "object" &&
|
|
4949
|
+
(message as { role?: unknown }).role === "assistant" &&
|
|
4950
|
+
(message as { stopReason?: unknown }).stopReason === "stop",
|
|
4951
|
+
);
|
|
4952
|
+
const finalText = finalAssistant ? acpFinalTextFromMessage(finalAssistant).text : "";
|
|
4953
|
+
rt.emitPromptLifecycle(correlation, {
|
|
4954
|
+
type: "agent_end",
|
|
4955
|
+
sessionId: id,
|
|
4956
|
+
...correlation,
|
|
4957
|
+
...(finalText ? { finalText } : {}),
|
|
4958
|
+
});
|
|
4959
|
+
}
|
|
4856
4960
|
}
|
|
4857
4961
|
} else {
|
|
4858
4962
|
rt.emitPromptLifecycle(undefined, { type: "agent_end", sessionId: id });
|
|
4859
4963
|
}
|
|
4860
4964
|
rt.activePromptCorrelation = undefined;
|
|
4861
|
-
terminalizeInFlightTools(rt, id, event.stopReason === "cancelled" ? "cancelled" : "
|
|
4965
|
+
terminalizeInFlightTools(rt, id, event.stopReason === "cancelled" ? "cancelled" : "failed");
|
|
4862
4966
|
try {
|
|
4863
4967
|
pushSessionFrame(rt, { type: "activity", sessionId: id, state: "idle" });
|
|
4864
4968
|
} catch (e) {
|
|
@@ -4976,14 +5080,13 @@ export function createNotificationsExtension(
|
|
|
4976
5080
|
// path and ordered before it — unlike message_update, which is queued async),
|
|
4977
5081
|
// then flushed here before the ask tool's execute calls registerAsk.
|
|
4978
5082
|
api.on("tool_execution_start", (event, ctx) => {
|
|
5083
|
+
const id = sessionId(ctx);
|
|
5084
|
+
const rt = runtimes.get(id);
|
|
5085
|
+
rt?.emitPromptEvent(event);
|
|
4979
5086
|
if (event.toolName === "ask") {
|
|
4980
|
-
const id = sessionId(ctx);
|
|
4981
|
-
const rt = runtimes.get(id);
|
|
4982
5087
|
if (!rt?.notificationsActive || rt.redact) return;
|
|
4983
5088
|
flushTurnText(rt, id, rt.currentTurnText, false);
|
|
4984
5089
|
}
|
|
4985
|
-
const id = sessionId(ctx);
|
|
4986
|
-
const rt = runtimes.get(id);
|
|
4987
5090
|
if (!rt?.notificationsActive || rt.redact) return;
|
|
4988
5091
|
rt.inFlightTools.set(event.toolCallId, { toolName: event.toolName, args: event.args });
|
|
4989
5092
|
try {
|
|
@@ -4999,12 +5102,32 @@ export function createNotificationsExtension(
|
|
|
4999
5102
|
}
|
|
5000
5103
|
});
|
|
5001
5104
|
|
|
5105
|
+
api.on("tool_execution_update", (event, ctx) => {
|
|
5106
|
+
const rt = runtimes.get(sessionId(ctx));
|
|
5107
|
+
rt?.emitPromptEvent(event);
|
|
5108
|
+
});
|
|
5109
|
+
|
|
5002
5110
|
api.on("tool_execution_end", (event, ctx) => {
|
|
5003
5111
|
const id = sessionId(ctx);
|
|
5004
5112
|
const rt = runtimes.get(id);
|
|
5005
5113
|
if (!rt) return;
|
|
5114
|
+
rt.emitPromptEvent(event);
|
|
5006
5115
|
const inFlight = rt.inFlightTools.get(event.toolCallId);
|
|
5007
|
-
if (!
|
|
5116
|
+
if (!inFlight) return;
|
|
5117
|
+
if (!rt.notificationsActive) {
|
|
5118
|
+
rt.inFlightTools.delete(event.toolCallId);
|
|
5119
|
+
return;
|
|
5120
|
+
}
|
|
5121
|
+
if (rt.policySuspended) {
|
|
5122
|
+
if (!inFlight.pendingPhase) {
|
|
5123
|
+
rt.inFlightTools.set(event.toolCallId, {
|
|
5124
|
+
toolName: inFlight.toolName,
|
|
5125
|
+
pendingPhase: event.isError ? "failed" : "completed",
|
|
5126
|
+
});
|
|
5127
|
+
}
|
|
5128
|
+
return;
|
|
5129
|
+
}
|
|
5130
|
+
if (rt.redact) {
|
|
5008
5131
|
rt.inFlightTools.delete(event.toolCallId);
|
|
5009
5132
|
return;
|
|
5010
5133
|
}
|
|
@@ -5172,10 +5295,22 @@ export function createNotificationsExtension(
|
|
|
5172
5295
|
await Promise.allSettled([...branchStartupTasks]);
|
|
5173
5296
|
const id = sessionId(ctx);
|
|
5174
5297
|
const rt = runtimes.get(id);
|
|
5175
|
-
if (rt) terminalizeInFlightTools(rt, id, "
|
|
5298
|
+
if (rt) terminalizeInFlightTools(rt, id, "cancelled");
|
|
5299
|
+
// Startup is only genuinely in flight when a `sessionStartPromises` entry
|
|
5300
|
+
// exists. Once startup has settled, the host is broker-visible and its
|
|
5301
|
+
// post-start `reconcileCurrentSession` may already have minted a
|
|
5302
|
+
// replacement notification-root token whose unregister is still awaiting
|
|
5303
|
+
// its file lock and atomic registry write. Returning before that settles
|
|
5304
|
+
// leaves a stale `sessions[id]` row that the retained older token is
|
|
5305
|
+
// correctly fenced from removing, so shutdown must join it.
|
|
5306
|
+
const startupWasPending = sessionStartPromises.has(id);
|
|
5176
5307
|
const controllerStop =
|
|
5177
5308
|
typeof ctx.sessionManager.getCwd === "function" ? controller.stopCurrentSession(ctx) : Promise.resolve(false);
|
|
5178
|
-
|
|
5309
|
+
const settledControllerStop = controllerStop.catch(error => {
|
|
5310
|
+
logger.warn(`notifications: controller shutdown failed: ${String(error)}`);
|
|
5311
|
+
return false;
|
|
5312
|
+
});
|
|
5313
|
+
if (startupWasPending) void settledControllerStop;
|
|
5179
5314
|
try {
|
|
5180
5315
|
await stopSession(id);
|
|
5181
5316
|
} catch (error) {
|
|
@@ -5186,5 +5321,10 @@ export function createNotificationsExtension(
|
|
|
5186
5321
|
// error severity (matching the postmortem cleanup precedent).
|
|
5187
5322
|
logger.error(`notifications: SDK notification runtime cleanup failed: ${String(error)}`);
|
|
5188
5323
|
}
|
|
5324
|
+
// Keep shutdown nonblocking only while native startup is genuinely
|
|
5325
|
+
// pending (the `/notify on` path); otherwise await the controller queue so
|
|
5326
|
+
// completed-start reconciliation and its replacement-token cleanup are
|
|
5327
|
+
// joined before lifecycle shutdown returns.
|
|
5328
|
+
if (!startupWasPending) await settledControllerStop;
|
|
5189
5329
|
});
|
|
5190
5330
|
}
|
|
@@ -116,6 +116,14 @@ export class RateLimitPool<T = unknown> {
|
|
|
116
116
|
return total;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/** Whether any queued item currently matches the predicate. */
|
|
120
|
+
someQueued(predicate: (item: RateLimitItem<T>) => boolean): boolean {
|
|
121
|
+
for (const queue of this.lanes.values()) {
|
|
122
|
+
if (queue.some(entry => predicate(entry.item))) return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
119
127
|
/** Current available token count (after refill at `now`). */
|
|
120
128
|
availableTokens(nowMs: number = this.now()): number {
|
|
121
129
|
this.refill(nowMs);
|
|
@@ -39,12 +39,14 @@ export const NOTIFICATION_PROTOCOL_VERSION = 3;
|
|
|
39
39
|
* Generation 28 rejects special files before retained native authority opens.
|
|
40
40
|
* Generation 29 adds serving-epoch compatibility, sidecar heartbeat, root GC,
|
|
41
41
|
* and Bot API cooldown structural fixes (#2956, #2960, #3048).
|
|
42
|
+
* Generation 30 adds opt-in tool activity delivery, closed lifecycle phases,
|
|
43
|
+
* and capability-versioned mixed-host compatibility.
|
|
42
44
|
*/
|
|
43
|
-
export const DAEMON_GENERATION =
|
|
45
|
+
export const DAEMON_GENERATION = 30;
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
48
|
* Serving-compatibility boundary for daemon lifecycle requests. Epoch 1 covers
|
|
47
|
-
* all builds published before this field existed;
|
|
48
|
-
* convergence and reload of compatible live predecessors.
|
|
49
|
+
* all builds published before this field existed; epoch 2 covered generation 29;
|
|
50
|
+
* bump this to force serving convergence and reload of compatible live predecessors.
|
|
49
51
|
*/
|
|
50
|
-
export const SERVING_EPOCH =
|
|
52
|
+
export const SERVING_EPOCH = 3;
|