@factory/droid-sdk 0.8.0 → 0.9.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/dist/{chunk-4G2RKM2H.js → chunk-FJP5OKS5.js} +1791 -1218
- package/dist/chunk-FJP5OKS5.js.map +1 -0
- package/dist/{chunk-ROFAQUG4.mjs → chunk-KXDG5ZWS.mjs} +1567 -994
- package/dist/chunk-KXDG5ZWS.mjs.map +1 -0
- package/dist/{index-_nUPGHpH.d.mts → index-BowvC0_E.d.mts} +21946 -491
- package/dist/{index-_nUPGHpH.d.ts → index-BowvC0_E.d.ts} +21946 -491
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/dist/node.d.mts +3 -2
- package/dist/node.d.ts +3 -2
- package/dist/node.js +402 -390
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +34 -22
- package/dist/node.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4G2RKM2H.js.map +0 -1
- package/dist/chunk-ROFAQUG4.mjs.map +0 -1
|
@@ -1874,8 +1874,6 @@ var ModelID = /* @__PURE__ */ ((ModelID2) => {
|
|
|
1874
1874
|
ModelID2["CLAUDE_FABLE_5"] = "claude-fable-5";
|
|
1875
1875
|
ModelID2["ATLAS_0721"] = "atlas-07-21";
|
|
1876
1876
|
ModelID2["ASTER_0715"] = "aster-07-15";
|
|
1877
|
-
ModelID2["AMBER_0709"] = "amber-07-09";
|
|
1878
|
-
ModelID2["AGATE_0711"] = "agate-07-11";
|
|
1879
1877
|
ModelID2["GPT_5"] = "gpt-5-2025-08-07";
|
|
1880
1878
|
ModelID2["GPT_5_MINI"] = "gpt-5-mini-2025-08-07";
|
|
1881
1879
|
ModelID2["GPT_5_NANO"] = "gpt-5-nano-2025-08-07";
|
|
@@ -1928,6 +1926,8 @@ var ModelID = /* @__PURE__ */ ((ModelID2) => {
|
|
|
1928
1926
|
ModelID2["GLM_5_1"] = "glm-5.1";
|
|
1929
1927
|
ModelID2["GLM_5_2"] = "glm-5.2";
|
|
1930
1928
|
ModelID2["GLM_5_2_FAST"] = "glm-5.2-fast";
|
|
1929
|
+
ModelID2["GLM_5_3"] = "glm-5.3";
|
|
1930
|
+
ModelID2["GLM_5_3_FLASH"] = "glm-5.3-flash";
|
|
1931
1931
|
ModelID2["INKLING"] = "inkling";
|
|
1932
1932
|
ModelID2["NEMOTRON_3_ULTRA"] = "nemotron-3-ultra";
|
|
1933
1933
|
ModelID2["SHIELD_RISK"] = "shield-risk";
|
|
@@ -2052,11 +2052,36 @@ var BugReportSourceSchema = z.object({
|
|
|
2052
2052
|
osVersion: z.string().max(100).optional()
|
|
2053
2053
|
});
|
|
2054
2054
|
var ToolExecutionModeSchema = z.nativeEnum(ToolExecutionMode);
|
|
2055
|
+
var SdkClientMetadataSchema = z.object({
|
|
2056
|
+
language: z.enum(["typescript", "python"]),
|
|
2057
|
+
version: z.string().min(1).max(64).regex(/^[A-Za-z0-9.+-]+$/)
|
|
2058
|
+
});
|
|
2059
|
+
function formatSdkClientMetadata(metadata) {
|
|
2060
|
+
return `${metadata.language}/${metadata.version}`;
|
|
2061
|
+
}
|
|
2062
|
+
var ClientRequestAttributionSchema = z.discriminatedUnion("client", [
|
|
2063
|
+
z.object({
|
|
2064
|
+
client: z.literal("sdk" /* SDK */),
|
|
2065
|
+
sdk: SdkClientMetadataSchema
|
|
2066
|
+
}),
|
|
2067
|
+
z.object({
|
|
2068
|
+
client: z.enum([
|
|
2069
|
+
"web-desktop" /* WebDesktop */,
|
|
2070
|
+
"web-app" /* WebApp */,
|
|
2071
|
+
"web-workspace" /* WebWorkspace */,
|
|
2072
|
+
"daemon" /* Daemon */,
|
|
2073
|
+
"cli" /* CLI */,
|
|
2074
|
+
"backend" /* Backend */
|
|
2075
|
+
])
|
|
2076
|
+
})
|
|
2077
|
+
]);
|
|
2055
2078
|
var TraceContextMetaSchema = z.object({
|
|
2056
2079
|
/** W3C traceparent header value (e.g., "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01") */
|
|
2057
2080
|
traceparent: z.string().optional(),
|
|
2058
2081
|
/** W3C tracestate header value for vendor-specific trace data */
|
|
2059
|
-
tracestate: z.string().optional()
|
|
2082
|
+
tracestate: z.string().optional(),
|
|
2083
|
+
/** Authenticated caller attribution injected by a daemon before worker dispatch. */
|
|
2084
|
+
requestAttribution: ClientRequestAttributionSchema.optional()
|
|
2060
2085
|
});
|
|
2061
2086
|
var JsonRpcEnvelopeSchema = z.object({
|
|
2062
2087
|
jsonrpc: z.literal(JSONRPC_VERSION),
|
|
@@ -2511,6 +2536,17 @@ var ListModelsOptionsSchema = z5.object({
|
|
|
2511
2536
|
var ListModelsResultSchema = z5.object({
|
|
2512
2537
|
models: z5.array(ModelInfoSchema)
|
|
2513
2538
|
});
|
|
2539
|
+
var ModelFallbackReasonSchema = z5.enum([
|
|
2540
|
+
/** A BYOK `custom:*` id with no matching entry in the machine's settings. */
|
|
2541
|
+
"custom_model_not_configured",
|
|
2542
|
+
/** The model is no longer available to the org that requested it. */
|
|
2543
|
+
"model_not_available_for_org"
|
|
2544
|
+
]);
|
|
2545
|
+
var ModelFallbackSchema = z5.object({
|
|
2546
|
+
/** The unusable id, as requested. */
|
|
2547
|
+
requestedModel: z5.string(),
|
|
2548
|
+
reason: ModelFallbackReasonSchema
|
|
2549
|
+
});
|
|
2514
2550
|
|
|
2515
2551
|
// ../droid-sdk-core/src/protocol/droid/schemas/mission-decomposition.ts
|
|
2516
2552
|
import z6 from "zod";
|
|
@@ -2808,7 +2844,9 @@ var LEGACY_COMPACTION_MODEL_IDS = [
|
|
|
2808
2844
|
"oxide-06-01",
|
|
2809
2845
|
"oxbow-06-01",
|
|
2810
2846
|
"owl-06-21",
|
|
2811
|
-
"gantry-05-07"
|
|
2847
|
+
"gantry-05-07",
|
|
2848
|
+
"amber-07-09",
|
|
2849
|
+
"agate-07-11"
|
|
2812
2850
|
];
|
|
2813
2851
|
var SHIELD_MODEL_ID_VALUES_SET = new Set(
|
|
2814
2852
|
SHIELD_MODEL_ID_VALUES
|
|
@@ -2878,7 +2916,10 @@ var McpOAuthConfigSchema = z10.union([
|
|
|
2878
2916
|
McpOAuthOptionsSchema
|
|
2879
2917
|
]);
|
|
2880
2918
|
var BuiltInModelIDSchema = z10.custom(
|
|
2881
|
-
(value) => typeof value === "string" && Object.values(ModelID).some((modelId) => modelId === value) && !ROUTER_MODEL_IDS.some((routerId) => routerId === value)
|
|
2919
|
+
(value) => typeof value === "string" && Object.values(ModelID).some((modelId) => modelId === value) && !ROUTER_MODEL_IDS.some((routerId) => routerId === value),
|
|
2920
|
+
{
|
|
2921
|
+
message: "Must be a supported built-in model ID."
|
|
2922
|
+
}
|
|
2882
2923
|
);
|
|
2883
2924
|
var ENV_VAR_REFERENCE_PATTERN = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
|
|
2884
2925
|
var CustomModelBedrockRequestMetadataSchema = z10.record(
|
|
@@ -3818,8 +3859,8 @@ var InitializeSessionRequestParamsSchema = z14.object({
|
|
|
3818
3859
|
sessionOriginHint: z14.nativeEnum(SessionOrigin).optional(),
|
|
3819
3860
|
tags: z14.array(SessionTagSchema).optional(),
|
|
3820
3861
|
privacyLevel: z14.enum(["private", "organization"]).optional(),
|
|
3821
|
-
//
|
|
3822
|
-
//
|
|
3862
|
+
// Active organization that owns the local session. The daemon validates
|
|
3863
|
+
// this against the authenticated connection before persisting it.
|
|
3823
3864
|
organizationId: z14.string().optional(),
|
|
3824
3865
|
title: z14.string().optional(),
|
|
3825
3866
|
/**
|
|
@@ -3886,6 +3927,12 @@ var InitializeSessionRequestParamsSchema = z14.object({
|
|
|
3886
3927
|
* (`<current-branch>-wt`) is preserved.
|
|
3887
3928
|
*/
|
|
3888
3929
|
worktreeBaseBranch: WorktreeGitRefSchema.optional(),
|
|
3930
|
+
/**
|
|
3931
|
+
* Whether `worktreeBaseBranch` was selected from origin before it existed
|
|
3932
|
+
* locally. The daemon fetches it and creates or copies it only when the
|
|
3933
|
+
* session starts.
|
|
3934
|
+
*/
|
|
3935
|
+
worktreeBaseBranchFromOrigin: z14.boolean().optional(),
|
|
3889
3936
|
/**
|
|
3890
3937
|
* Explicit branch name to create for the worktree, chosen by the user
|
|
3891
3938
|
* through the branch selector's "Create branch" action. When provided,
|
|
@@ -3903,7 +3950,12 @@ var InitializeSessionRequestParamsSchema = z14.object({
|
|
|
3903
3950
|
* ID of the setup profile to execute in the worktree after creation.
|
|
3904
3951
|
* Must match a profile stored on the target daemon for the project.
|
|
3905
3952
|
*/
|
|
3906
|
-
worktreeSetupProfileId: z14.string().uuid().optional()
|
|
3953
|
+
worktreeSetupProfileId: z14.string().uuid().optional(),
|
|
3954
|
+
/** Cross-machine parent lineage supplied by a trusted daemon. */
|
|
3955
|
+
callingMetadata: z14.object({
|
|
3956
|
+
callingSessionId: z14.string().min(1),
|
|
3957
|
+
callingToolUseId: z14.string().min(1)
|
|
3958
|
+
}).optional()
|
|
3907
3959
|
}).merge(ToolOverrideParamsSchema);
|
|
3908
3960
|
var LoadSessionRequestParamsSchema = z14.object({
|
|
3909
3961
|
sessionId: z14.string(),
|
|
@@ -3940,6 +3992,7 @@ var LoadSessionRequestParamsSchema = z14.object({
|
|
|
3940
3992
|
var AddUserMessageRequestParamsSchema = z14.object({
|
|
3941
3993
|
messageId: z14.string().optional(),
|
|
3942
3994
|
text: z14.string(),
|
|
3995
|
+
content: z14.array(z14.union([TextBlockSchema, ImageBlockSchema])).min(1).optional(),
|
|
3943
3996
|
images: Base64ImageSourceSchema.array().optional(),
|
|
3944
3997
|
imagePaths: z14.string().array().optional(),
|
|
3945
3998
|
files: DocumentSourceSchema.array().optional(),
|
|
@@ -4906,7 +4959,8 @@ var DaemonConnectionMetadataSchema = z15.object({
|
|
|
4906
4959
|
connectAttemptId: z15.string().optional(),
|
|
4907
4960
|
/** Low-cardinality trigger for the root computer-connect episode. */
|
|
4908
4961
|
connectAttemptTrigger: z15.string().optional()
|
|
4909
|
-
}).optional()
|
|
4962
|
+
}).optional(),
|
|
4963
|
+
sdk: SdkClientMetadataSchema.optional()
|
|
4910
4964
|
});
|
|
4911
4965
|
var DaemonAuthenticateRequestParamsSchema = z15.object({
|
|
4912
4966
|
/** WorkOS JWT access token */
|
|
@@ -4930,7 +4984,13 @@ var DaemonAuthenticateRequestParamsSchema = z15.object({
|
|
|
4930
4984
|
message: "Either token or apiKey must be provided"
|
|
4931
4985
|
}).refine((data) => !data.actAsGrant || Boolean(data.token), {
|
|
4932
4986
|
message: "actAsGrant requires token"
|
|
4933
|
-
})
|
|
4987
|
+
}).refine(
|
|
4988
|
+
(data) => data.caller === "sdk" /* SDK */ === (data.metadata?.sdk !== void 0),
|
|
4989
|
+
{
|
|
4990
|
+
message: "SDK callers must provide SDK metadata",
|
|
4991
|
+
path: ["metadata", "sdk"]
|
|
4992
|
+
}
|
|
4993
|
+
);
|
|
4934
4994
|
var DaemonAuthenticateRequestSchema = JsonRpcBaseRequestSchema.extend({
|
|
4935
4995
|
method: z15.literal("daemon.authenticate" /* AUTHENTICATE */),
|
|
4936
4996
|
params: DaemonAuthenticateRequestParamsSchema
|
|
@@ -5427,8 +5487,9 @@ function settleToolExecutionLifecyclePhase(phase) {
|
|
|
5427
5487
|
}
|
|
5428
5488
|
|
|
5429
5489
|
// ../droid-sdk-core/src/protocol/droid/schemas/constants.ts
|
|
5430
|
-
var FACTORY_PROTOCOL_VERSION = "1.
|
|
5490
|
+
var FACTORY_PROTOCOL_VERSION = "1.193.0";
|
|
5431
5491
|
var FACTORY_CLIENT_HEADER = "X-Factory-Client";
|
|
5492
|
+
var FACTORY_SDK_HEADER = "X-Factory-Sdk";
|
|
5432
5493
|
var FACTORY_CLIENT_VERSION = "X-Client-Version";
|
|
5433
5494
|
var LEGACY_FACTORY_API_VERSION2 = LEGACY_FACTORY_API_VERSION;
|
|
5434
5495
|
var JSONRPC_VERSION2 = JSONRPC_VERSION;
|
|
@@ -5820,7 +5881,19 @@ var DaemonRunAutomationResultSchema = z20.object({
|
|
|
5820
5881
|
/** Template this automation was created from, when known. */
|
|
5821
5882
|
templateId: z20.nativeEnum(AutomationTemplateId).optional(),
|
|
5822
5883
|
cwd: z20.string(),
|
|
5884
|
+
/**
|
|
5885
|
+
* The model to run on. Omitted when the automation pins no model, and also
|
|
5886
|
+
* when its configured model could not be resolved on this machine, in which
|
|
5887
|
+
* case `modelFallback` explains the omission and the run uses the daemon
|
|
5888
|
+
* default.
|
|
5889
|
+
*/
|
|
5823
5890
|
model: z20.string().optional(),
|
|
5891
|
+
/**
|
|
5892
|
+
* Set when the automation's configured model was dropped because it does not
|
|
5893
|
+
* resolve on this machine. The run still proceeds on the daemon default;
|
|
5894
|
+
* callers surface this so the substitution is not silent.
|
|
5895
|
+
*/
|
|
5896
|
+
modelFallback: ModelFallbackSchema.optional(),
|
|
5824
5897
|
/** Optional reasoning effort for the run session. */
|
|
5825
5898
|
reasoningEffort: z20.string().optional(),
|
|
5826
5899
|
/**
|
|
@@ -5894,7 +5967,13 @@ var AutomationRunRecordSchema = z20.object({
|
|
|
5894
5967
|
isRetry: z20.boolean().optional(),
|
|
5895
5968
|
originalRunId: z20.string().optional(),
|
|
5896
5969
|
/** Droid session that executed this run, when dispatched as a real session. */
|
|
5897
|
-
sessionId: z20.string().optional()
|
|
5970
|
+
sessionId: z20.string().optional(),
|
|
5971
|
+
/**
|
|
5972
|
+
* Set when this run's configured model was dropped at spawn time. Scheduled
|
|
5973
|
+
* runs fire without a caller to receive the run-time result, so the history
|
|
5974
|
+
* record is the only place the substitution can surface for them.
|
|
5975
|
+
*/
|
|
5976
|
+
modelFallback: ModelFallbackSchema.optional()
|
|
5898
5977
|
});
|
|
5899
5978
|
var DaemonGetAutomationHistoryResultSchema = z20.object({
|
|
5900
5979
|
automationId: z20.string(),
|
|
@@ -6906,6 +6985,7 @@ var SoftwareFactoryWorkstreamStateSchema = z28.enum([
|
|
|
6906
6985
|
"draft",
|
|
6907
6986
|
"active",
|
|
6908
6987
|
"paused",
|
|
6988
|
+
// Retained for wire compatibility with older clients. New writes archive.
|
|
6909
6989
|
"canceled",
|
|
6910
6990
|
"archived"
|
|
6911
6991
|
]);
|
|
@@ -6940,12 +7020,15 @@ var SoftwareFactoryWorkstreamSchema = z28.object({
|
|
|
6940
7020
|
icon: z28.string().optional(),
|
|
6941
7021
|
state: SoftwareFactoryWorkstreamStateSchema,
|
|
6942
7022
|
userId: z28.string().optional(),
|
|
6943
|
-
/**
|
|
7023
|
+
/**
|
|
7024
|
+
* Team the workstream is attributed to. A label, not a visibility control:
|
|
7025
|
+
* every workstream is readable by its whole org.
|
|
7026
|
+
*/
|
|
6944
7027
|
owningTeamId: z28.string().optional(),
|
|
6945
7028
|
/**
|
|
6946
|
-
*
|
|
6947
|
-
*
|
|
6948
|
-
*
|
|
7029
|
+
* Retired: workstreams are org-visible, so the backend no longer reports an
|
|
7030
|
+
* access mode. Present only on rows written before the mode was retired, and
|
|
7031
|
+
* kept in the schema because the protocol forbids removing a field.
|
|
6949
7032
|
*/
|
|
6950
7033
|
accessMode: z28.enum(["team", "private"]).optional(),
|
|
6951
7034
|
creationSessionId: z28.string().optional(),
|
|
@@ -6964,6 +7047,9 @@ var SoftwareFactoryWorkstreamSchema = z28.object({
|
|
|
6964
7047
|
dashboardPort: z28.number().int().optional(),
|
|
6965
7048
|
/** Public URL of the workstream's published Conjure dashboard site. */
|
|
6966
7049
|
dashboardUrl: z28.string().optional(),
|
|
7050
|
+
/** Pause proposed changes for human approval before investigation. */
|
|
7051
|
+
requireChangeApproval: z28.boolean().optional(),
|
|
7052
|
+
/** Pause completed investigation specifications for human approval. */
|
|
6967
7053
|
requireApproval: z28.boolean(),
|
|
6968
7054
|
reviewers: z28.array(z28.string()),
|
|
6969
7055
|
createdAt: z28.string(),
|
|
@@ -7005,7 +7091,7 @@ var SoftwareFactoryActivitySchema = z28.object({
|
|
|
7005
7091
|
id: z28.string(),
|
|
7006
7092
|
changeId: z28.string(),
|
|
7007
7093
|
workstreamId: z28.string(),
|
|
7008
|
-
kind: z28.enum(["investigation", "implementation", "steward"]),
|
|
7094
|
+
kind: z28.enum(["change_review", "investigation", "implementation", "steward"]),
|
|
7009
7095
|
status: z28.enum(["pending", "in_progress", "completed", "failed", "canceled"]),
|
|
7010
7096
|
reviewStatus: z28.enum(["awaiting", "approved", "declined", "commented"]).optional(),
|
|
7011
7097
|
reviewComment: z28.string().optional(),
|
|
@@ -7087,15 +7173,15 @@ var DaemonSfCreateWorkstreamRequestParamsSchema = z28.object({
|
|
|
7087
7173
|
*/
|
|
7088
7174
|
userId: z28.string().optional(),
|
|
7089
7175
|
/**
|
|
7090
|
-
*
|
|
7091
|
-
*
|
|
7092
|
-
*
|
|
7176
|
+
* Team to attribute the workstream to. When set, the backend validates the
|
|
7177
|
+
* acting principal is linked to this team, stamps it, and issues the owner
|
|
7178
|
+
* grant. It does not restrict who can read the workstream.
|
|
7093
7179
|
*/
|
|
7094
7180
|
owningTeamId: z28.string().optional(),
|
|
7095
7181
|
/**
|
|
7096
|
-
*
|
|
7097
|
-
*
|
|
7098
|
-
*
|
|
7182
|
+
* Accepted and ignored: workstreams are visible to the whole org, so the
|
|
7183
|
+
* daemon drops this rather than forwarding it. Kept in the schema because the
|
|
7184
|
+
* protocol forbids removing a field, and so an older client still validates.
|
|
7099
7185
|
*/
|
|
7100
7186
|
accessMode: z28.enum(["team", "private"]).optional(),
|
|
7101
7187
|
/** Creation session that owns the bootstrap interview. */
|
|
@@ -7106,6 +7192,7 @@ var DaemonSfCreateWorkstreamRequestParamsSchema = z28.object({
|
|
|
7106
7192
|
repos: z28.array(z28.string()).optional(),
|
|
7107
7193
|
workerConcurrency: z28.number().optional(),
|
|
7108
7194
|
stewardConcurrency: z28.number().optional(),
|
|
7195
|
+
requireChangeApproval: z28.boolean().optional(),
|
|
7109
7196
|
requireApproval: z28.boolean().optional(),
|
|
7110
7197
|
reviewers: z28.array(z28.string()).optional()
|
|
7111
7198
|
});
|
|
@@ -7138,6 +7225,7 @@ var DaemonSfUpdateWorkstreamRequestParamsSchema = z28.object({
|
|
|
7138
7225
|
stewardConcurrency: z28.number().optional(),
|
|
7139
7226
|
dashboardPort: z28.number().optional(),
|
|
7140
7227
|
dashboardUrl: z28.string().optional(),
|
|
7228
|
+
requireChangeApproval: z28.boolean().optional(),
|
|
7141
7229
|
requireApproval: z28.boolean().optional(),
|
|
7142
7230
|
reviewers: z28.array(z28.string()).optional()
|
|
7143
7231
|
});
|
|
@@ -7162,7 +7250,7 @@ var DaemonSfDeleteWorkstreamResultSchema = z28.object({
|
|
|
7162
7250
|
automationsDeleted: z28.number(),
|
|
7163
7251
|
/** Whether the workstream directory existed and was removed. */
|
|
7164
7252
|
workstreamDirDeleted: z28.boolean(),
|
|
7165
|
-
/** Number of remote (backend-scheduled) automations
|
|
7253
|
+
/** Number of linked remote (backend-scheduled) automations that were soft-deleted. */
|
|
7166
7254
|
remoteAutomationsDeleted: z28.number().optional()
|
|
7167
7255
|
});
|
|
7168
7256
|
var DaemonSfListEventsRequestParamsSchema = z28.object({
|
|
@@ -7595,7 +7683,9 @@ var DaemonInitializeSessionRequestParamsSchema = InitializeSessionRequestParamsS
|
|
|
7595
7683
|
* message so worktree creation can honour it: a worktree that cuts a new
|
|
7596
7684
|
* branch from its base starts from the base's freshly fetched remote ref
|
|
7597
7685
|
* instead of the possibly stale local one. The pull that mutates an
|
|
7598
|
-
* existing checkout still rides on the first message, not on init.
|
|
7686
|
+
* existing checkout still rides on the first message, not on init. This
|
|
7687
|
+
* controls an update from origin; origin-only checkout creation uses
|
|
7688
|
+
* `createFromOrigin` on the checkout request instead.
|
|
7599
7689
|
*/
|
|
7600
7690
|
pullFromOrigin: z31.boolean().optional()
|
|
7601
7691
|
});
|
|
@@ -7618,7 +7708,8 @@ var DaemonAddUserMessageRequestParamsSchema = AddUserMessageRequestParamsSchema.
|
|
|
7618
7708
|
* this message to the agent. Set by session-launch callers on the first
|
|
7619
7709
|
* message only; a failed pull is not fatal, it arrives as its own
|
|
7620
7710
|
* LLM-only message ahead of this one so the agent resolves it without the
|
|
7621
|
-
* user's own text being touched.
|
|
7711
|
+
* user's own text being touched. This updates the checked-out branch; it
|
|
7712
|
+
* does not create an origin-only branch locally.
|
|
7622
7713
|
*/
|
|
7623
7714
|
pullFromOrigin: z31.boolean().optional()
|
|
7624
7715
|
});
|
|
@@ -7789,6 +7880,7 @@ var DaemonAvailableSessionInfoSchema = z31.object({
|
|
|
7789
7880
|
// Parent session ID for child/worker sessions (from JSONL summary `callingSessionId` field)
|
|
7790
7881
|
callingToolUseId: z31.string().optional(),
|
|
7791
7882
|
// Parent tool use ID that spawned this child session (from JSONL summary)
|
|
7883
|
+
organizationId: z31.string().optional(),
|
|
7792
7884
|
tags: z31.array(SessionTagSchema).optional(),
|
|
7793
7885
|
// Session tags for categorization
|
|
7794
7886
|
// Drop only the worktree metadata (not the whole session, and not the whole
|
|
@@ -8031,11 +8123,41 @@ var DaemonRenameSessionRequestSchema = JsonRpcBaseRequestSchema.extend(
|
|
|
8031
8123
|
);
|
|
8032
8124
|
var DaemonRenameSessionResponseSchema = createAckCompatibleResponseSchema(DaemonRenameSessionResultSchema);
|
|
8033
8125
|
var DaemonListGitBranchesRequestParamsSchema = z31.object({
|
|
8034
|
-
cwd: z31.string()
|
|
8126
|
+
cwd: z31.string(),
|
|
8127
|
+
/**
|
|
8128
|
+
* Whether to discover branches that exist only on origin. Optional so newer
|
|
8129
|
+
* clients can continue listing local branches through older request shapes.
|
|
8130
|
+
*/
|
|
8131
|
+
includeOriginBranches: z31.boolean().optional()
|
|
8132
|
+
});
|
|
8133
|
+
var DaemonCheckoutGitBranchConflictDataSchema = z31.object({
|
|
8134
|
+
reason: z31.literal("branch_occupied_by_managed_worktree"),
|
|
8135
|
+
worktree: z31.discriminatedUnion("lifecycle", [
|
|
8136
|
+
SessionWorktreeMetadataSchema.pick({
|
|
8137
|
+
path: true,
|
|
8138
|
+
repoRoot: true
|
|
8139
|
+
}).required().extend({
|
|
8140
|
+
lifecycle: z31.literal("ephemeral" /* Ephemeral */),
|
|
8141
|
+
sessionId: z31.string()
|
|
8142
|
+
}),
|
|
8143
|
+
SessionWorktreeMetadataSchema.pick({
|
|
8144
|
+
path: true,
|
|
8145
|
+
repoRoot: true
|
|
8146
|
+
}).required().extend({
|
|
8147
|
+
lifecycle: z31.literal("persistent" /* Persistent */)
|
|
8148
|
+
})
|
|
8149
|
+
])
|
|
8035
8150
|
});
|
|
8036
8151
|
var DaemonListGitBranchesResultSchema = z31.object({
|
|
8037
8152
|
isGitRepository: z31.boolean(),
|
|
8038
8153
|
branches: z31.array(z31.string()),
|
|
8154
|
+
/**
|
|
8155
|
+
* Branches found on origin that do not yet exist locally. Optional for
|
|
8156
|
+
* compatibility with daemons predating remote branch discovery.
|
|
8157
|
+
*/
|
|
8158
|
+
originBranches: z31.array(z31.string()).optional(),
|
|
8159
|
+
/** Whether origin branch discovery is still refreshing in the background. */
|
|
8160
|
+
originBranchesPending: z31.boolean().optional(),
|
|
8039
8161
|
currentBranch: z31.string().nullable(),
|
|
8040
8162
|
/** The repository's default branch, when it has one. */
|
|
8041
8163
|
defaultBranch: z31.string().optional(),
|
|
@@ -8046,7 +8168,9 @@ var DaemonListGitBranchesResultSchema = z31.object({
|
|
|
8046
8168
|
checkedOutBranches: z31.array(
|
|
8047
8169
|
z31.object({
|
|
8048
8170
|
branch: z31.string(),
|
|
8049
|
-
path: z31.string()
|
|
8171
|
+
path: z31.string(),
|
|
8172
|
+
/** Branch-switch conflict details for a Factory-managed worktree. */
|
|
8173
|
+
conflict: DaemonCheckoutGitBranchConflictDataSchema.optional()
|
|
8050
8174
|
})
|
|
8051
8175
|
).optional()
|
|
8052
8176
|
});
|
|
@@ -8061,32 +8185,26 @@ var DaemonCheckoutGitBranchRequestParamsSchema = z31.object({
|
|
|
8061
8185
|
cwd: z31.string(),
|
|
8062
8186
|
branch: z31.string(),
|
|
8063
8187
|
create: z31.boolean().optional(),
|
|
8188
|
+
/**
|
|
8189
|
+
* Create a local tracking branch from the same-named origin branch. This
|
|
8190
|
+
* describes where the local branch comes from; it does not request a pull.
|
|
8191
|
+
*/
|
|
8192
|
+
createFromOrigin: z31.boolean().optional(),
|
|
8193
|
+
/**
|
|
8194
|
+
* Pull the newly checked-out branch before returning. For an origin-only
|
|
8195
|
+
* selection, use this with `createFromOrigin`.
|
|
8196
|
+
*/
|
|
8197
|
+
pullFromOrigin: z31.boolean().optional(),
|
|
8064
8198
|
resolution: z31.enum(["stash", "commit"]).optional()
|
|
8065
8199
|
});
|
|
8066
|
-
var DaemonCheckoutGitBranchConflictDataSchema = z31.object({
|
|
8067
|
-
reason: z31.literal("branch_occupied_by_managed_worktree"),
|
|
8068
|
-
worktree: z31.discriminatedUnion("lifecycle", [
|
|
8069
|
-
SessionWorktreeMetadataSchema.pick({
|
|
8070
|
-
path: true,
|
|
8071
|
-
repoRoot: true
|
|
8072
|
-
}).required().extend({
|
|
8073
|
-
lifecycle: z31.literal("ephemeral" /* Ephemeral */),
|
|
8074
|
-
sessionId: z31.string()
|
|
8075
|
-
}),
|
|
8076
|
-
SessionWorktreeMetadataSchema.pick({
|
|
8077
|
-
path: true,
|
|
8078
|
-
repoRoot: true
|
|
8079
|
-
}).required().extend({
|
|
8080
|
-
lifecycle: z31.literal("persistent" /* Persistent */)
|
|
8081
|
-
})
|
|
8082
|
-
])
|
|
8083
|
-
});
|
|
8084
8200
|
var DaemonCheckoutGitBranchResultSchema = z31.discriminatedUnion(
|
|
8085
8201
|
"status",
|
|
8086
8202
|
[
|
|
8087
8203
|
z31.object({
|
|
8088
8204
|
status: z31.literal("checked_out"),
|
|
8089
|
-
currentBranch: z31.string()
|
|
8205
|
+
currentBranch: z31.string(),
|
|
8206
|
+
/** Scrubbed git output when an active-session origin pull failed. */
|
|
8207
|
+
pullFailure: z31.string().optional()
|
|
8090
8208
|
}),
|
|
8091
8209
|
z31.object({
|
|
8092
8210
|
status: z31.literal("needs_resolution"),
|
|
@@ -9181,10 +9299,18 @@ var SESSION_INIT_TIMEOUT = 6e4;
|
|
|
9181
9299
|
var MCP_AUTH_TIMEOUT = 3e5;
|
|
9182
9300
|
var COMPACTION_TIMEOUT = 24e4;
|
|
9183
9301
|
var REWIND_TIMEOUT = 6e4;
|
|
9184
|
-
var SDK_VERSION = "0.
|
|
9302
|
+
var SDK_VERSION = "0.9.0";
|
|
9303
|
+
var SDK_CLIENT_METADATA = {
|
|
9304
|
+
language: "typescript",
|
|
9305
|
+
version: SDK_VERSION
|
|
9306
|
+
};
|
|
9307
|
+
var SDK_REQUEST_ATTRIBUTION = {
|
|
9308
|
+
client: "sdk" /* SDK */,
|
|
9309
|
+
sdk: SDK_CLIENT_METADATA
|
|
9310
|
+
};
|
|
9185
9311
|
var SDK_TAG = {
|
|
9186
9312
|
name: "sdk",
|
|
9187
|
-
metadata:
|
|
9313
|
+
metadata: SDK_CLIENT_METADATA
|
|
9188
9314
|
};
|
|
9189
9315
|
|
|
9190
9316
|
// src/schemas/constants.ts
|
|
@@ -9343,6 +9469,9 @@ function mergeSessionSettings(current, patch) {
|
|
|
9343
9469
|
}
|
|
9344
9470
|
return SessionSettingsSchema2.parse(merged);
|
|
9345
9471
|
}
|
|
9472
|
+
function withSdkTag(tags = []) {
|
|
9473
|
+
return [...tags.filter((tag) => tag.name !== SDK_TAG.name), SDK_TAG];
|
|
9474
|
+
}
|
|
9346
9475
|
function unwrapResponseResult(response) {
|
|
9347
9476
|
if (response.error) {
|
|
9348
9477
|
throw new ProtocolError(response.error.message, {
|
|
@@ -10164,21 +10293,27 @@ function isPendingToolResultMarker(block) {
|
|
|
10164
10293
|
}
|
|
10165
10294
|
function buildUserMessageContentBlocks({
|
|
10166
10295
|
text,
|
|
10296
|
+
content,
|
|
10167
10297
|
images,
|
|
10168
10298
|
files,
|
|
10169
10299
|
trimText = false,
|
|
10170
10300
|
includeEmptyText = true
|
|
10171
10301
|
}) {
|
|
10172
|
-
const blocks = [];
|
|
10173
|
-
|
|
10174
|
-
|
|
10302
|
+
const blocks = content?.length ? [...content] : [];
|
|
10303
|
+
const hasExplicitContent = blocks.length > 0;
|
|
10304
|
+
if (!hasExplicitContent) {
|
|
10305
|
+
for (const image of images ?? []) {
|
|
10306
|
+
blocks.push({ type: "image" /* Image */, source: image });
|
|
10307
|
+
}
|
|
10175
10308
|
}
|
|
10176
10309
|
for (const file of files ?? []) {
|
|
10177
10310
|
blocks.push({ type: "document" /* Document */, source: file });
|
|
10178
10311
|
}
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10312
|
+
if (!hasExplicitContent) {
|
|
10313
|
+
const value = trimText ? text?.trim() : text;
|
|
10314
|
+
if (value !== void 0 && (includeEmptyText || value.length > 0)) {
|
|
10315
|
+
blocks.push({ type: "text" /* Text */, text: value });
|
|
10316
|
+
}
|
|
10182
10317
|
}
|
|
10183
10318
|
return blocks;
|
|
10184
10319
|
}
|
|
@@ -12856,7 +12991,8 @@ async function connectWithRetry({
|
|
|
12856
12991
|
ensureRunning,
|
|
12857
12992
|
providerType,
|
|
12858
12993
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
12859
|
-
connectAttemptContext
|
|
12994
|
+
connectAttemptContext,
|
|
12995
|
+
onAttempts
|
|
12860
12996
|
}) {
|
|
12861
12997
|
if (ensureRunning) {
|
|
12862
12998
|
await OtelTracing.trace("computer.ensure_running" /* COMPUTER_ENSURE_RUNNING */, ensureRunning, {
|
|
@@ -12914,6 +13050,12 @@ async function connectWithRetry({
|
|
|
12914
13050
|
OtelTracing.setActiveSpanAttributes({
|
|
12915
13051
|
["factory.daemon.connect_attempts" /* FACTORY_DAEMON_CONNECT_ATTEMPTS */]: attempts
|
|
12916
13052
|
});
|
|
13053
|
+
if (attempts > 0) {
|
|
13054
|
+
try {
|
|
13055
|
+
onAttempts?.(attempts);
|
|
13056
|
+
} catch {
|
|
13057
|
+
}
|
|
13058
|
+
}
|
|
12917
13059
|
}
|
|
12918
13060
|
}
|
|
12919
13061
|
|
|
@@ -14295,7 +14437,7 @@ function createWebSocketDaemonClient(config) {
|
|
|
14295
14437
|
}
|
|
14296
14438
|
|
|
14297
14439
|
// ../daemon-sdk/src/session/DaemonSessionController.ts
|
|
14298
|
-
import { EventEmitter as
|
|
14440
|
+
import { EventEmitter as EventEmitter4 } from "eventemitter3";
|
|
14299
14441
|
import { v4 as uuidv47 } from "uuid";
|
|
14300
14442
|
|
|
14301
14443
|
// ../daemon-sdk/src/session/connection-status.ts
|
|
@@ -14376,7 +14518,7 @@ var MessageRouter = class _MessageRouter extends EventEmitter3 {
|
|
|
14376
14518
|
requestId: msg.id,
|
|
14377
14519
|
sessionId: msg.params.sessionId,
|
|
14378
14520
|
method: msg.method,
|
|
14379
|
-
|
|
14521
|
+
count: msg.params.toolUses.length
|
|
14380
14522
|
});
|
|
14381
14523
|
this.emit("permissionRequest", {
|
|
14382
14524
|
request: msg,
|
|
@@ -14403,7 +14545,7 @@ var MessageRouter = class _MessageRouter extends EventEmitter3 {
|
|
|
14403
14545
|
requestId: msg.id,
|
|
14404
14546
|
sessionId: msg.params.sessionId,
|
|
14405
14547
|
method: msg.method,
|
|
14406
|
-
|
|
14548
|
+
count: msg.params.questions.length,
|
|
14407
14549
|
toolCallId: msg.params.toolCallId
|
|
14408
14550
|
});
|
|
14409
14551
|
this.emit("askUserRequest", {
|
|
@@ -14515,7 +14657,7 @@ var MessageRouter = class _MessageRouter extends EventEmitter3 {
|
|
|
14515
14657
|
"type"
|
|
14516
14658
|
]);
|
|
14517
14659
|
logInfo("[MessageRouter] Received unsupported session notification", {
|
|
14518
|
-
|
|
14660
|
+
type: notificationType ?? void 0,
|
|
14519
14661
|
isError: !!protocolVersionMismatch,
|
|
14520
14662
|
version: protocolVersionMismatch?.localFactoryProtocolVersion,
|
|
14521
14663
|
sessionId: sessionId ?? void 0
|
|
@@ -14637,507 +14779,148 @@ var ReconnectionStrategy = class {
|
|
|
14637
14779
|
}
|
|
14638
14780
|
};
|
|
14639
14781
|
|
|
14640
|
-
// ../daemon-sdk/src/session/
|
|
14641
|
-
|
|
14642
|
-
|
|
14643
|
-
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
14648
|
-
|
|
14649
|
-
|
|
14650
|
-
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
|
|
14655
|
-
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
}
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
_AskUserRequestHandler.validateResult(pending, result);
|
|
14680
|
-
this.pendingRequests.delete(id);
|
|
14681
|
-
this.emit("askUserResolved", id, result);
|
|
14682
|
-
}
|
|
14683
|
-
clearAskUser(id) {
|
|
14684
|
-
const pending = this.pendingRequests.get(id);
|
|
14685
|
-
if (!pending) {
|
|
14686
|
-
logWarn(
|
|
14687
|
-
"[AskUserRequestHandler] No pending ask-user request found to clear",
|
|
14688
|
-
{
|
|
14689
|
-
requestId: id
|
|
14690
|
-
}
|
|
14691
|
-
);
|
|
14692
|
-
return;
|
|
14693
|
-
}
|
|
14694
|
-
this.pendingRequests.delete(id);
|
|
14695
|
-
this.emit("askUserResolved", id);
|
|
14782
|
+
// ../daemon-sdk/src/session/state/enums.ts
|
|
14783
|
+
var QueuedUserMessageKind = /* @__PURE__ */ ((QueuedUserMessageKind2) => {
|
|
14784
|
+
QueuedUserMessageKind2["LocalDeferredAfterEsc"] = "local_deferred_after_esc";
|
|
14785
|
+
QueuedUserMessageKind2["LocalPausedAfterEsc"] = "local_paused_after_esc";
|
|
14786
|
+
QueuedUserMessageKind2["DaemonQueuedDiscardable"] = "daemon_queued_discardable";
|
|
14787
|
+
QueuedUserMessageKind2["DaemonQueuedEndOfLoop"] = "daemon_queued_end_of_loop";
|
|
14788
|
+
QueuedUserMessageKind2["LocalDeferredDuringManualCompaction"] = "local_deferred_during_manual_compaction";
|
|
14789
|
+
return QueuedUserMessageKind2;
|
|
14790
|
+
})(QueuedUserMessageKind || {});
|
|
14791
|
+
var QueuedUserMessageDisplayGroup = /* @__PURE__ */ ((QueuedUserMessageDisplayGroup2) => {
|
|
14792
|
+
QueuedUserMessageDisplayGroup2["Steering"] = "steering";
|
|
14793
|
+
QueuedUserMessageDisplayGroup2["Queued"] = "queued";
|
|
14794
|
+
return QueuedUserMessageDisplayGroup2;
|
|
14795
|
+
})(QueuedUserMessageDisplayGroup || {});
|
|
14796
|
+
|
|
14797
|
+
// ../daemon-sdk/src/session/state/queuedUserMessageHelpers.ts
|
|
14798
|
+
var QUEUED_MESSAGE_KIND_METADATA = {
|
|
14799
|
+
["local_deferred_after_esc" /* LocalDeferredAfterEsc */]: {
|
|
14800
|
+
displayGroup: "steering" /* Steering */
|
|
14801
|
+
},
|
|
14802
|
+
["local_paused_after_esc" /* LocalPausedAfterEsc */]: {
|
|
14803
|
+
reviewable: true,
|
|
14804
|
+
displayGroup: "queued" /* Queued */,
|
|
14805
|
+
reviewPriority: 1
|
|
14806
|
+
},
|
|
14807
|
+
["daemon_queued_discardable" /* DaemonQueuedDiscardable */]: {
|
|
14808
|
+
daemonBacked: true,
|
|
14809
|
+
reviewable: true,
|
|
14810
|
+
displayGroup: "steering" /* Steering */,
|
|
14811
|
+
reviewPriority: 0
|
|
14812
|
+
},
|
|
14813
|
+
["daemon_queued_end_of_loop" /* DaemonQueuedEndOfLoop */]: {
|
|
14814
|
+
daemonBacked: true,
|
|
14815
|
+
reviewable: true,
|
|
14816
|
+
displayGroup: "queued" /* Queued */,
|
|
14817
|
+
reviewPriority: 1
|
|
14818
|
+
},
|
|
14819
|
+
["local_deferred_during_manual_compaction" /* LocalDeferredDuringManualCompaction */]: {
|
|
14820
|
+
displayGroup: "queued" /* Queued */
|
|
14696
14821
|
}
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
|
|
14822
|
+
};
|
|
14823
|
+
function isDaemonQueuedMessageKind(kind) {
|
|
14824
|
+
return QUEUED_MESSAGE_KIND_METADATA[kind].daemonBacked === true;
|
|
14825
|
+
}
|
|
14826
|
+
function isReviewableQueuedMessageKind(kind) {
|
|
14827
|
+
return QUEUED_MESSAGE_KIND_METADATA[kind].reviewable === true;
|
|
14828
|
+
}
|
|
14829
|
+
function getQueuedUserMessageDisplayGroup(kind) {
|
|
14830
|
+
return QUEUED_MESSAGE_KIND_METADATA[kind].displayGroup;
|
|
14831
|
+
}
|
|
14832
|
+
function getQueuedUserMessageReviewPriority(kind) {
|
|
14833
|
+
return QUEUED_MESSAGE_KIND_METADATA[kind].reviewPriority ?? null;
|
|
14834
|
+
}
|
|
14835
|
+
function getQueuedUserMessageKindForQueuePlacement(queuePlacement) {
|
|
14836
|
+
return queuePlacement === "end_of_loop" /* EndOfLoop */ ? "daemon_queued_end_of_loop" /* DaemonQueuedEndOfLoop */ : "daemon_queued_discardable" /* DaemonQueuedDiscardable */;
|
|
14837
|
+
}
|
|
14838
|
+
|
|
14839
|
+
// ../daemon-sdk/src/session/queueLoadReconciliation.ts
|
|
14840
|
+
function getSendableContentFromBlocks(content) {
|
|
14841
|
+
const images = [];
|
|
14842
|
+
const files = [];
|
|
14843
|
+
const textBlocks = [];
|
|
14844
|
+
const orderedContent = [];
|
|
14845
|
+
for (const block of content) {
|
|
14846
|
+
if (block.type === "image" /* Image */) {
|
|
14847
|
+
images.push(block.source);
|
|
14848
|
+
orderedContent.push(block);
|
|
14849
|
+
} else if (block.type === "document" /* Document */) {
|
|
14850
|
+
files.push(block.source);
|
|
14851
|
+
} else if (block.type === "text" /* Text */) {
|
|
14852
|
+
textBlocks.push(block.text);
|
|
14853
|
+
orderedContent.push(block);
|
|
14703
14854
|
}
|
|
14704
14855
|
}
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
}
|
|
14711
|
-
|
|
14712
|
-
|
|
14856
|
+
return {
|
|
14857
|
+
text: textBlocks.join("\n").trim(),
|
|
14858
|
+
images,
|
|
14859
|
+
files,
|
|
14860
|
+
orderedContent
|
|
14861
|
+
};
|
|
14862
|
+
}
|
|
14863
|
+
function getQueuePlacementForQueuedMessageKind(kind) {
|
|
14864
|
+
return kind === "daemon_queued_end_of_loop" /* DaemonQueuedEndOfLoop */ ? "end_of_loop" /* EndOfLoop */ : "end_of_turn" /* EndOfTurn */;
|
|
14865
|
+
}
|
|
14866
|
+
function documentSourcesMatch(left, right) {
|
|
14867
|
+
if (left.type !== right.type || left.mediaType !== right.mediaType) {
|
|
14868
|
+
return false;
|
|
14713
14869
|
}
|
|
14714
|
-
|
|
14715
|
-
|
|
14870
|
+
if (left.type === "base64" /* Base64 */ && right.type === "base64" /* Base64 */) {
|
|
14871
|
+
return left.data === right.data && left.name === right.name && left.parsedData === right.parsedData && left.path === right.path;
|
|
14716
14872
|
}
|
|
14717
|
-
|
|
14718
|
-
|
|
14719
|
-
this.removeAllListeners();
|
|
14873
|
+
if (left.type === "text" /* Text */ && right.type === "text" /* Text */) {
|
|
14874
|
+
return left.data === right.data && left.name === right.name && left.mime === right.mime;
|
|
14720
14875
|
}
|
|
14721
|
-
|
|
14722
|
-
|
|
14723
|
-
|
|
14876
|
+
return false;
|
|
14877
|
+
}
|
|
14878
|
+
function imageSourcesMatch(left, right) {
|
|
14879
|
+
return left.type === right.type && left.data === right.data && left.mediaType === right.mediaType;
|
|
14880
|
+
}
|
|
14881
|
+
function arrayItemsMatch(left, right, matches) {
|
|
14882
|
+
return left.length === right.length && left.every((leftItem, index) => matches(leftItem, right[index]));
|
|
14883
|
+
}
|
|
14884
|
+
function orderedContentBlocksMatch(left, right) {
|
|
14885
|
+
return arrayItemsMatch(left, right, (leftBlock, rightBlock) => {
|
|
14886
|
+
if (leftBlock.type !== rightBlock.type) return false;
|
|
14887
|
+
if (leftBlock.type === "text" /* Text */ && rightBlock.type === "text" /* Text */) {
|
|
14888
|
+
return leftBlock.text === rightBlock.text;
|
|
14724
14889
|
}
|
|
14725
|
-
|
|
14726
|
-
|
|
14727
|
-
);
|
|
14728
|
-
const answered = /* @__PURE__ */ new Set();
|
|
14729
|
-
for (const answer of result.answers) {
|
|
14730
|
-
const question = questionsByIndex.get(answer.index);
|
|
14731
|
-
if (!question) {
|
|
14732
|
-
throw new MetaError("Answer references unknown question index", {
|
|
14733
|
-
requestId: pending.requestId,
|
|
14734
|
-
index: answer.index
|
|
14735
|
-
});
|
|
14736
|
-
}
|
|
14737
|
-
if (answered.has(answer.index)) {
|
|
14738
|
-
throw new MetaError("Duplicate answers for question index", {
|
|
14739
|
-
requestId: pending.requestId,
|
|
14740
|
-
index: answer.index
|
|
14741
|
-
});
|
|
14742
|
-
}
|
|
14743
|
-
answered.add(answer.index);
|
|
14744
|
-
}
|
|
14745
|
-
const missing = pending.questions.map((q) => q.index).filter((idx) => !answered.has(idx));
|
|
14746
|
-
if (missing.length > 0) {
|
|
14747
|
-
throw new MetaError("Missing answers for some questions", {
|
|
14748
|
-
requestId: pending.requestId,
|
|
14749
|
-
missing: missing.map(String)
|
|
14750
|
-
});
|
|
14751
|
-
}
|
|
14752
|
-
}
|
|
14753
|
-
};
|
|
14754
|
-
|
|
14755
|
-
// ../daemon-sdk/src/session/handlers/PendingUserActionStore.ts
|
|
14756
|
-
var PendingUserActionStore = class {
|
|
14757
|
-
permissionsByToolUseId = /* @__PURE__ */ new Map();
|
|
14758
|
-
askUsersBySession = /* @__PURE__ */ new Map();
|
|
14759
|
-
savePermission(action) {
|
|
14760
|
-
this.permissionsByToolUseId.set(action.toolUseId, action);
|
|
14761
|
-
logInfo("[PendingUserActionStore] Stored permission for later replay", {
|
|
14762
|
-
sessionId: action.sessionId,
|
|
14763
|
-
requestId: action.requestId,
|
|
14764
|
-
toolUseId: action.toolUseId,
|
|
14765
|
-
selectedOptionLabel: action.selectedOption
|
|
14766
|
-
});
|
|
14767
|
-
}
|
|
14768
|
-
saveAskUser(action) {
|
|
14769
|
-
let map = this.askUsersBySession.get(action.sessionId);
|
|
14770
|
-
if (!map) {
|
|
14771
|
-
map = /* @__PURE__ */ new Map();
|
|
14772
|
-
this.askUsersBySession.set(action.sessionId, map);
|
|
14773
|
-
}
|
|
14774
|
-
map.set(action.toolCallId, action);
|
|
14775
|
-
logInfo("[PendingUserActionStore] Stored ask-user for later replay", {
|
|
14776
|
-
sessionId: action.sessionId,
|
|
14777
|
-
requestId: action.requestId,
|
|
14778
|
-
toolCallId: action.toolCallId
|
|
14779
|
-
});
|
|
14780
|
-
}
|
|
14781
|
-
/** Peek a buffered permission response by its globally-unique tool-use id. */
|
|
14782
|
-
getForToolUse(toolUseId) {
|
|
14783
|
-
return this.permissionsByToolUseId.get(toolUseId);
|
|
14784
|
-
}
|
|
14785
|
-
getForToolCall(sessionId, toolCallId) {
|
|
14786
|
-
return this.askUsersBySession.get(sessionId)?.get(toolCallId);
|
|
14787
|
-
}
|
|
14788
|
-
/** Remove and return a buffered permission response by tool-use id. */
|
|
14789
|
-
takeForToolUse(toolUseId) {
|
|
14790
|
-
const action = this.permissionsByToolUseId.get(toolUseId);
|
|
14791
|
-
if (!action) return void 0;
|
|
14792
|
-
this.permissionsByToolUseId.delete(toolUseId);
|
|
14793
|
-
return action;
|
|
14794
|
-
}
|
|
14795
|
-
takeForToolCall(sessionId, toolCallId) {
|
|
14796
|
-
const map = this.askUsersBySession.get(sessionId);
|
|
14797
|
-
if (!map) return void 0;
|
|
14798
|
-
const action = map.get(toolCallId);
|
|
14799
|
-
if (!action) return void 0;
|
|
14800
|
-
map.delete(toolCallId);
|
|
14801
|
-
if (map.size === 0) this.askUsersBySession.delete(sessionId);
|
|
14802
|
-
return action;
|
|
14803
|
-
}
|
|
14804
|
-
getPermissionsForSession(sessionId) {
|
|
14805
|
-
return Array.from(this.permissionsByToolUseId.values()).filter(
|
|
14806
|
-
(action) => action.sessionId === sessionId
|
|
14807
|
-
);
|
|
14808
|
-
}
|
|
14809
|
-
getAskUsersForSession(sessionId) {
|
|
14810
|
-
const map = this.askUsersBySession.get(sessionId);
|
|
14811
|
-
return map ? Array.from(map.values()) : [];
|
|
14812
|
-
}
|
|
14813
|
-
clearSession(sessionId) {
|
|
14814
|
-
let permCount = 0;
|
|
14815
|
-
for (const [toolUseId, action] of this.permissionsByToolUseId) {
|
|
14816
|
-
if (action.sessionId === sessionId) {
|
|
14817
|
-
this.permissionsByToolUseId.delete(toolUseId);
|
|
14818
|
-
permCount += 1;
|
|
14819
|
-
}
|
|
14820
|
-
}
|
|
14821
|
-
const askCount = this.askUsersBySession.get(sessionId)?.size ?? 0;
|
|
14822
|
-
this.askUsersBySession.delete(sessionId);
|
|
14823
|
-
const total = permCount + askCount;
|
|
14824
|
-
if (total > 0) {
|
|
14825
|
-
logInfo("[PendingUserActionStore] Cleared session", {
|
|
14826
|
-
sessionId,
|
|
14827
|
-
pendingRequestCount: permCount,
|
|
14828
|
-
questionCount: askCount
|
|
14829
|
-
});
|
|
14890
|
+
if (leftBlock.type === "image" /* Image */ && rightBlock.type === "image" /* Image */) {
|
|
14891
|
+
return imageSourcesMatch(leftBlock.source, rightBlock.source) && leftBlock.generated === rightBlock.generated;
|
|
14830
14892
|
}
|
|
14831
|
-
return
|
|
14832
|
-
}
|
|
14833
|
-
clearAll() {
|
|
14834
|
-
this.permissionsByToolUseId.clear();
|
|
14835
|
-
this.askUsersBySession.clear();
|
|
14836
|
-
}
|
|
14837
|
-
destroy() {
|
|
14838
|
-
this.clearAll();
|
|
14839
|
-
}
|
|
14840
|
-
};
|
|
14841
|
-
|
|
14842
|
-
// ../daemon-sdk/src/session/handlers/PermissionRequestHandler.ts
|
|
14843
|
-
import { EventEmitter as EventEmitter5 } from "eventemitter3";
|
|
14844
|
-
function normalizeAssociatedSessionIds(sessionId, associatedSessionIds) {
|
|
14845
|
-
return Array.from(/* @__PURE__ */ new Set([sessionId, ...associatedSessionIds ?? []]));
|
|
14893
|
+
return false;
|
|
14894
|
+
});
|
|
14846
14895
|
}
|
|
14847
|
-
|
|
14848
|
-
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
|
|
14852
|
-
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
requestId: id,
|
|
14861
|
-
sessionId,
|
|
14862
|
-
toolCount: request.params.toolUses.length,
|
|
14863
|
-
options: request.params.options
|
|
14864
|
-
});
|
|
14865
|
-
const { options } = request.params;
|
|
14866
|
-
const toolUses = request.params.toolUses.map((tool) => ({
|
|
14867
|
-
...tool,
|
|
14868
|
-
toolUse: {
|
|
14869
|
-
...tool.toolUse,
|
|
14870
|
-
input: getPermissionToolInputForDisplay2({
|
|
14871
|
-
confirmationType: tool.confirmationType,
|
|
14872
|
-
details: tool.details,
|
|
14873
|
-
toolInput: tool.toolUse.input
|
|
14874
|
-
})
|
|
14875
|
-
}
|
|
14876
|
-
}));
|
|
14877
|
-
const requestAssociatedSessionIds = normalizeAssociatedSessionIds(
|
|
14878
|
-
sessionId,
|
|
14879
|
-
request.params.associatedSessionIds
|
|
14880
|
-
);
|
|
14881
|
-
const existingPermission = this.pendingPermissions.get(id);
|
|
14882
|
-
const associatedSessionIds = existingPermission ? normalizeAssociatedSessionIds(sessionId, [
|
|
14883
|
-
...existingPermission.associatedSessionIds,
|
|
14884
|
-
...requestAssociatedSessionIds
|
|
14885
|
-
]) : requestAssociatedSessionIds;
|
|
14886
|
-
const pendingPermission = {
|
|
14887
|
-
requestId: id,
|
|
14888
|
-
sessionId,
|
|
14889
|
-
associatedSessionIds,
|
|
14890
|
-
toolUses,
|
|
14891
|
-
options,
|
|
14892
|
-
timestamp: Date.now(),
|
|
14893
|
-
...context?.isRestored ? { isRestored: true } : {}
|
|
14894
|
-
};
|
|
14895
|
-
if (existingPermission) {
|
|
14896
|
-
this.pendingPermissions.set(id, pendingPermission);
|
|
14897
|
-
const addedSurface = associatedSessionIds.some(
|
|
14898
|
-
(associatedSessionId) => !existingPermission.associatedSessionIds.includes(associatedSessionId)
|
|
14899
|
-
);
|
|
14900
|
-
logInfo(
|
|
14901
|
-
"[PermissionRequestHandler] Duplicate permission request replay",
|
|
14902
|
-
{
|
|
14903
|
-
requestId: id,
|
|
14904
|
-
sessionId
|
|
14905
|
-
}
|
|
14906
|
-
);
|
|
14907
|
-
if (addedSurface) {
|
|
14908
|
-
this.emit("permissionRequested", pendingPermission);
|
|
14909
|
-
}
|
|
14910
|
-
return;
|
|
14911
|
-
}
|
|
14912
|
-
const incomingToolUseIds = new Set(
|
|
14913
|
-
toolUses.map((toolUse) => toolUse.toolUse.id)
|
|
14914
|
-
);
|
|
14915
|
-
for (const [pendingId, pending] of this.pendingPermissions.entries()) {
|
|
14916
|
-
if (pending.toolUses.some(
|
|
14917
|
-
(toolUse) => incomingToolUseIds.has(toolUse.toolUse.id)
|
|
14918
|
-
)) {
|
|
14919
|
-
this.clearPermission(pendingId, { emitResolved: true });
|
|
14920
|
-
}
|
|
14921
|
-
}
|
|
14922
|
-
this.pendingPermissions.set(id, pendingPermission);
|
|
14923
|
-
this.statistics.total++;
|
|
14924
|
-
logInfo("[PermissionRequestHandler] Stored pending permission", {
|
|
14925
|
-
requestId: id,
|
|
14926
|
-
totalCount: this.pendingPermissions.size
|
|
14927
|
-
});
|
|
14928
|
-
logInfo("[PermissionRequestHandler] Emitting permissionRequested event", {
|
|
14929
|
-
requestId: id
|
|
14930
|
-
});
|
|
14931
|
-
this.emit("permissionRequested", pendingPermission);
|
|
14932
|
-
}
|
|
14933
|
-
/**
|
|
14934
|
-
* Resolve a permission request with user's choice
|
|
14935
|
-
*/
|
|
14936
|
-
resolvePermission(id, option) {
|
|
14937
|
-
const pending = this.pendingPermissions.get(id);
|
|
14938
|
-
if (!pending) {
|
|
14939
|
-
throw new MetaError("No pending permission found", { requestId: id });
|
|
14940
|
-
}
|
|
14941
|
-
const isValidOption = pending.options.some((opt) => opt.value === option);
|
|
14942
|
-
if (!isValidOption) {
|
|
14943
|
-
const allowedOptions = pending.options.map((opt) => opt.value);
|
|
14944
|
-
throw new MetaError(
|
|
14945
|
-
`Invalid permission option: ${option}. Allowed: ${allowedOptions.join(", ")}`,
|
|
14946
|
-
{
|
|
14947
|
-
requestId: id
|
|
14948
|
-
}
|
|
14949
|
-
);
|
|
14950
|
-
}
|
|
14951
|
-
this.pendingPermissions.delete(id);
|
|
14952
|
-
if (option !== "cancel" /* Cancel */) {
|
|
14953
|
-
this.statistics.approved++;
|
|
14954
|
-
} else {
|
|
14955
|
-
this.statistics.denied++;
|
|
14956
|
-
}
|
|
14957
|
-
this.emit("permissionResolved", id, option);
|
|
14958
|
-
}
|
|
14959
|
-
/**
|
|
14960
|
-
* Clear a specific permission without an outcome.
|
|
14961
|
-
*/
|
|
14962
|
-
clearPermission(id, options) {
|
|
14963
|
-
this.pendingPermissions.delete(id);
|
|
14964
|
-
if (options?.emitResolved) {
|
|
14965
|
-
this.emit("permissionResolved", id);
|
|
14966
|
-
}
|
|
14967
|
-
}
|
|
14968
|
-
/**
|
|
14969
|
-
* Clear all pending permissions for a specific session.
|
|
14970
|
-
*
|
|
14971
|
-
* With `preserveRelayed`, permissions surfaced on multiple sessions (a
|
|
14972
|
-
* relayed subagent permission whose `associatedSessionIds` extend beyond this
|
|
14973
|
-
* session) are kept. loadSession uses this so reloading the parent doesn't
|
|
14974
|
-
* wipe a child's still-pending prompt that the daemon's parent-session load
|
|
14975
|
-
* never returns.
|
|
14976
|
-
*/
|
|
14977
|
-
clearSessionPermissions(sessionId, options) {
|
|
14978
|
-
const permissionsToRemove = [];
|
|
14979
|
-
for (const [id, permission] of this.pendingPermissions.entries()) {
|
|
14980
|
-
if (permission.sessionId !== sessionId) {
|
|
14981
|
-
continue;
|
|
14982
|
-
}
|
|
14983
|
-
if (options?.preserveRelayed && permission.associatedSessionIds.some(
|
|
14984
|
-
(associatedId) => associatedId !== sessionId
|
|
14985
|
-
)) {
|
|
14986
|
-
continue;
|
|
14987
|
-
}
|
|
14988
|
-
permissionsToRemove.push(id);
|
|
14989
|
-
}
|
|
14990
|
-
for (const id of permissionsToRemove) {
|
|
14991
|
-
this.clearPermission(id, options);
|
|
14992
|
-
}
|
|
14993
|
-
logInfo("[PermissionRequestHandler] Cleared permissions for session", {
|
|
14994
|
-
sessionId,
|
|
14995
|
-
count: permissionsToRemove.length
|
|
14896
|
+
function hasCanonicalFallbackOrder(content) {
|
|
14897
|
+
const fallbackContent = [
|
|
14898
|
+
...content.images.map(
|
|
14899
|
+
(source) => ({
|
|
14900
|
+
type: "image" /* Image */,
|
|
14901
|
+
source
|
|
14902
|
+
})
|
|
14903
|
+
)
|
|
14904
|
+
];
|
|
14905
|
+
if (content.text) {
|
|
14906
|
+
fallbackContent.push({
|
|
14907
|
+
type: "text" /* Text */,
|
|
14908
|
+
text: content.text
|
|
14996
14909
|
});
|
|
14997
14910
|
}
|
|
14998
|
-
|
|
14999
|
-
* Clear all pending permissions
|
|
15000
|
-
*/
|
|
15001
|
-
clearAllPermissions() {
|
|
15002
|
-
this.pendingPermissions.clear();
|
|
15003
|
-
}
|
|
15004
|
-
/**
|
|
15005
|
-
* Get all pending permissions
|
|
15006
|
-
*/
|
|
15007
|
-
getPendingPermissions() {
|
|
15008
|
-
return Array.from(this.pendingPermissions.values());
|
|
15009
|
-
}
|
|
15010
|
-
getPendingPermissionsForSession(sessionId) {
|
|
15011
|
-
return this.getPendingPermissions().filter(
|
|
15012
|
-
(permission) => permission.associatedSessionIds.includes(sessionId)
|
|
15013
|
-
);
|
|
15014
|
-
}
|
|
15015
|
-
/**
|
|
15016
|
-
* Get a specific pending permission
|
|
15017
|
-
*/
|
|
15018
|
-
getPendingPermission(id) {
|
|
15019
|
-
return this.pendingPermissions.get(id);
|
|
15020
|
-
}
|
|
15021
|
-
/**
|
|
15022
|
-
* Check if there are any pending permissions
|
|
15023
|
-
*/
|
|
15024
|
-
hasPendingPermissions() {
|
|
15025
|
-
return this.pendingPermissions.size > 0;
|
|
15026
|
-
}
|
|
15027
|
-
/**
|
|
15028
|
-
* Destroy and clean up
|
|
15029
|
-
*/
|
|
15030
|
-
destroy() {
|
|
15031
|
-
this.clearAllPermissions();
|
|
15032
|
-
this.removeAllListeners();
|
|
15033
|
-
}
|
|
15034
|
-
};
|
|
15035
|
-
|
|
15036
|
-
// ../daemon-sdk/src/session/state/enums.ts
|
|
15037
|
-
var QueuedUserMessageKind = /* @__PURE__ */ ((QueuedUserMessageKind2) => {
|
|
15038
|
-
QueuedUserMessageKind2["LocalDeferredAfterEsc"] = "local_deferred_after_esc";
|
|
15039
|
-
QueuedUserMessageKind2["LocalPausedAfterEsc"] = "local_paused_after_esc";
|
|
15040
|
-
QueuedUserMessageKind2["DaemonQueuedDiscardable"] = "daemon_queued_discardable";
|
|
15041
|
-
QueuedUserMessageKind2["DaemonQueuedEndOfLoop"] = "daemon_queued_end_of_loop";
|
|
15042
|
-
QueuedUserMessageKind2["LocalDeferredDuringManualCompaction"] = "local_deferred_during_manual_compaction";
|
|
15043
|
-
return QueuedUserMessageKind2;
|
|
15044
|
-
})(QueuedUserMessageKind || {});
|
|
15045
|
-
var QueuedUserMessageDisplayGroup = /* @__PURE__ */ ((QueuedUserMessageDisplayGroup2) => {
|
|
15046
|
-
QueuedUserMessageDisplayGroup2["Steering"] = "steering";
|
|
15047
|
-
QueuedUserMessageDisplayGroup2["Queued"] = "queued";
|
|
15048
|
-
return QueuedUserMessageDisplayGroup2;
|
|
15049
|
-
})(QueuedUserMessageDisplayGroup || {});
|
|
15050
|
-
|
|
15051
|
-
// ../daemon-sdk/src/session/state/queuedUserMessageHelpers.ts
|
|
15052
|
-
var QUEUED_MESSAGE_KIND_METADATA = {
|
|
15053
|
-
["local_deferred_after_esc" /* LocalDeferredAfterEsc */]: {
|
|
15054
|
-
displayGroup: "steering" /* Steering */
|
|
15055
|
-
},
|
|
15056
|
-
["local_paused_after_esc" /* LocalPausedAfterEsc */]: {
|
|
15057
|
-
reviewable: true,
|
|
15058
|
-
displayGroup: "queued" /* Queued */,
|
|
15059
|
-
reviewPriority: 1
|
|
15060
|
-
},
|
|
15061
|
-
["daemon_queued_discardable" /* DaemonQueuedDiscardable */]: {
|
|
15062
|
-
daemonBacked: true,
|
|
15063
|
-
reviewable: true,
|
|
15064
|
-
displayGroup: "steering" /* Steering */,
|
|
15065
|
-
reviewPriority: 0
|
|
15066
|
-
},
|
|
15067
|
-
["daemon_queued_end_of_loop" /* DaemonQueuedEndOfLoop */]: {
|
|
15068
|
-
daemonBacked: true,
|
|
15069
|
-
reviewable: true,
|
|
15070
|
-
displayGroup: "queued" /* Queued */,
|
|
15071
|
-
reviewPriority: 1
|
|
15072
|
-
},
|
|
15073
|
-
["local_deferred_during_manual_compaction" /* LocalDeferredDuringManualCompaction */]: {
|
|
15074
|
-
displayGroup: "queued" /* Queued */
|
|
15075
|
-
}
|
|
15076
|
-
};
|
|
15077
|
-
function isDaemonQueuedMessageKind(kind) {
|
|
15078
|
-
return QUEUED_MESSAGE_KIND_METADATA[kind].daemonBacked === true;
|
|
15079
|
-
}
|
|
15080
|
-
function isReviewableQueuedMessageKind(kind) {
|
|
15081
|
-
return QUEUED_MESSAGE_KIND_METADATA[kind].reviewable === true;
|
|
15082
|
-
}
|
|
15083
|
-
function getQueuedUserMessageDisplayGroup(kind) {
|
|
15084
|
-
return QUEUED_MESSAGE_KIND_METADATA[kind].displayGroup;
|
|
15085
|
-
}
|
|
15086
|
-
function getQueuedUserMessageReviewPriority(kind) {
|
|
15087
|
-
return QUEUED_MESSAGE_KIND_METADATA[kind].reviewPriority ?? null;
|
|
15088
|
-
}
|
|
15089
|
-
function getQueuedUserMessageKindForQueuePlacement(queuePlacement) {
|
|
15090
|
-
return queuePlacement === "end_of_loop" /* EndOfLoop */ ? "daemon_queued_end_of_loop" /* DaemonQueuedEndOfLoop */ : "daemon_queued_discardable" /* DaemonQueuedDiscardable */;
|
|
15091
|
-
}
|
|
15092
|
-
|
|
15093
|
-
// ../daemon-sdk/src/session/queueLoadReconciliation.ts
|
|
15094
|
-
function getSendableContentFromBlocks(content) {
|
|
15095
|
-
const images = [];
|
|
15096
|
-
const files = [];
|
|
15097
|
-
const textBlocks = [];
|
|
15098
|
-
for (const block of content) {
|
|
15099
|
-
if (block.type === "image" /* Image */) {
|
|
15100
|
-
images.push(block.source);
|
|
15101
|
-
} else if (block.type === "document" /* Document */) {
|
|
15102
|
-
files.push(block.source);
|
|
15103
|
-
} else if (block.type === "text" /* Text */) {
|
|
15104
|
-
textBlocks.push(block.text);
|
|
15105
|
-
}
|
|
15106
|
-
}
|
|
15107
|
-
return {
|
|
15108
|
-
text: textBlocks.join("\n").trim(),
|
|
15109
|
-
images,
|
|
15110
|
-
files
|
|
15111
|
-
};
|
|
15112
|
-
}
|
|
15113
|
-
function getQueuePlacementForQueuedMessageKind(kind) {
|
|
15114
|
-
return kind === "daemon_queued_end_of_loop" /* DaemonQueuedEndOfLoop */ ? "end_of_loop" /* EndOfLoop */ : "end_of_turn" /* EndOfTurn */;
|
|
15115
|
-
}
|
|
15116
|
-
function documentSourcesMatch(left, right) {
|
|
15117
|
-
if (left.type !== right.type || left.mediaType !== right.mediaType) {
|
|
15118
|
-
return false;
|
|
15119
|
-
}
|
|
15120
|
-
if (left.type === "base64" /* Base64 */ && right.type === "base64" /* Base64 */) {
|
|
15121
|
-
return left.data === right.data && left.name === right.name && left.parsedData === right.parsedData && left.path === right.path;
|
|
15122
|
-
}
|
|
15123
|
-
if (left.type === "text" /* Text */ && right.type === "text" /* Text */) {
|
|
15124
|
-
return left.data === right.data && left.name === right.name && left.mime === right.mime;
|
|
15125
|
-
}
|
|
15126
|
-
return false;
|
|
15127
|
-
}
|
|
15128
|
-
function imageSourcesMatch(left, right) {
|
|
15129
|
-
return left.type === right.type && left.data === right.data && left.mediaType === right.mediaType;
|
|
15130
|
-
}
|
|
15131
|
-
function arrayItemsMatch(left, right, matches) {
|
|
15132
|
-
return left.length === right.length && left.every((leftItem, index) => matches(leftItem, right[index]));
|
|
14911
|
+
return orderedContentBlocksMatch(content.orderedContent, fallbackContent);
|
|
15133
14912
|
}
|
|
15134
14913
|
function sendableContentMatches(left, right) {
|
|
15135
|
-
|
|
14914
|
+
const fallbackFieldsMatch = left.text === right.text && arrayItemsMatch(left.images, right.images, imageSourcesMatch) && arrayItemsMatch(left.files, right.files, documentSourcesMatch);
|
|
14915
|
+
if (!fallbackFieldsMatch) return false;
|
|
14916
|
+
if (left.files.length > 0 || right.files.length > 0) return true;
|
|
14917
|
+
return hasCanonicalFallbackOrder(left) && hasCanonicalFallbackOrder(right) || orderedContentBlocksMatch(left.orderedContent, right.orderedContent);
|
|
15136
14918
|
}
|
|
15137
14919
|
function buildStaleQueuedMessageResubmitParams(message) {
|
|
15138
14920
|
const content = getSendableContentFromBlocks(message.content);
|
|
15139
14921
|
return {
|
|
15140
14922
|
text: content.text,
|
|
14923
|
+
...content.files.length === 0 && !hasCanonicalFallbackOrder(content) ? { content: content.orderedContent } : {},
|
|
15141
14924
|
...content.images.length > 0 ? { images: content.images } : {},
|
|
15142
14925
|
...content.files.length > 0 ? { files: content.files } : {},
|
|
15143
14926
|
queuePlacement: getQueuePlacementForQueuedMessageKind(message.kind)
|
|
@@ -15176,6 +14959,7 @@ async function reconcileQueuedMessagesAfterLoad(params) {
|
|
|
15176
14959
|
(queuedMsg) => {
|
|
15177
14960
|
const content = buildUserMessageContentBlocks2({
|
|
15178
14961
|
text: queuedMsg.text,
|
|
14962
|
+
content: queuedMsg.content,
|
|
15179
14963
|
images: queuedMsg.images,
|
|
15180
14964
|
files: queuedMsg.files
|
|
15181
14965
|
});
|
|
@@ -15320,29 +15104,42 @@ function taskInvocationStatusForCompletionReason(reason) {
|
|
|
15320
15104
|
return "failed" /* Failed */;
|
|
15321
15105
|
}
|
|
15322
15106
|
}
|
|
15323
|
-
var DaemonSessionController = class _DaemonSessionController extends
|
|
15107
|
+
var DaemonSessionController = class _DaemonSessionController extends EventEmitter4 {
|
|
15324
15108
|
daemonClient;
|
|
15325
15109
|
messageRouter;
|
|
15326
|
-
|
|
15110
|
+
// Session-scoped prompt state (pending permissions, pending ask-user
|
|
15111
|
+
// requests, buffered answers) lives on the shared app-lifetime
|
|
15112
|
+
// MultiSessionStateManager, not on this per-connection controller, so it
|
|
15113
|
+
// survives controller teardown/recreation. These accessors keep call sites
|
|
15114
|
+
// unchanged.
|
|
15115
|
+
get permissionHandler() {
|
|
15116
|
+
return this.sessionStateManager.permissionRequests;
|
|
15117
|
+
}
|
|
15327
15118
|
// Per-session permission subscribers. A subagent (child Task) permission is
|
|
15328
15119
|
// fanned out to every subscribed session in its `associatedSessionIds`, so a
|
|
15329
15120
|
// child's prompt surfaces on both the child and its parent.
|
|
15330
15121
|
sessionPermissionSubscribers = /* @__PURE__ */ new Map();
|
|
15331
|
-
askUserHandler
|
|
15122
|
+
get askUserHandler() {
|
|
15123
|
+
return this.sessionStateManager.askUserRequests;
|
|
15124
|
+
}
|
|
15332
15125
|
// Buffered prompt responses for inactive sessions.
|
|
15333
|
-
pendingUserActionStore
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
//
|
|
15337
|
-
//
|
|
15338
|
-
//
|
|
15339
|
-
//
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
//
|
|
15126
|
+
get pendingUserActionStore() {
|
|
15127
|
+
return this.sessionStateManager.pendingUserActions;
|
|
15128
|
+
}
|
|
15129
|
+
// Teardown hooks for this controller's listeners on the shared prompt
|
|
15130
|
+
// stores. The stores outlive the controller, so destroy() must detach
|
|
15131
|
+
// exactly this controller's listeners (removeAllListeners would detach
|
|
15132
|
+
// other controllers' too).
|
|
15133
|
+
promptStoreUnsubscribes = [];
|
|
15134
|
+
// Whether a session's worker is paused (buffer responses), whether a session
|
|
15135
|
+
// is draining a concurrent prompt batch (suppress resume-driven auto-clear),
|
|
15136
|
+
// and the spawn options replayed on self-resume all describe the *session*,
|
|
15137
|
+
// not this connection, so they live on the shared app-lifetime
|
|
15138
|
+
// MultiSessionStateManager and survive controller teardown/recreation.
|
|
15139
|
+
// Sessions with a self-resume loadSession already in flight. Stays on this
|
|
15140
|
+
// controller: it dedupes an in-flight RPC/retry loop on this connection's
|
|
15141
|
+
// daemonClient, not session state.
|
|
15343
15142
|
selfResumeInFlight = /* @__PURE__ */ new Set();
|
|
15344
|
-
// Last loadSession spawn options, reused for self-resume.
|
|
15345
|
-
sessionLoadOptions = /* @__PURE__ */ new Map();
|
|
15346
15143
|
// addUserMessage responses can resolve after their CREATE_MESSAGE notification.
|
|
15347
15144
|
// Track that ordering so an already-confirmed turn is not added to the
|
|
15348
15145
|
// visible queue after its real user message has been rendered.
|
|
@@ -15400,9 +15197,6 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
15400
15197
|
this.messageRouter = new MessageRouter({
|
|
15401
15198
|
emitReceiveSpans: config.clientType !== "cli"
|
|
15402
15199
|
});
|
|
15403
|
-
this.permissionHandler = new PermissionRequestHandler();
|
|
15404
|
-
this.askUserHandler = new AskUserRequestHandler();
|
|
15405
|
-
this.pendingUserActionStore = new PendingUserActionStore();
|
|
15406
15200
|
this.sessionStateManager = sessionStateManager;
|
|
15407
15201
|
this.missionStateManager = missionStateManager ?? null;
|
|
15408
15202
|
const reconnectDelegated = config.machineType === "local" /* Local */ ? config.reconnectDelegated ?? false : false;
|
|
@@ -15453,12 +15247,17 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
15453
15247
|
this.connectAttemptContext = previous;
|
|
15454
15248
|
}
|
|
15455
15249
|
}
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
|
|
15460
|
-
|
|
15461
|
-
|
|
15250
|
+
getFactoryClientType() {
|
|
15251
|
+
switch (this.getClientType()) {
|
|
15252
|
+
case "desktop":
|
|
15253
|
+
return "web-desktop" /* WebDesktop */;
|
|
15254
|
+
case "cli":
|
|
15255
|
+
return "cli" /* CLI */;
|
|
15256
|
+
case "sdk":
|
|
15257
|
+
return "sdk" /* SDK */;
|
|
15258
|
+
case "web":
|
|
15259
|
+
return "web-app" /* WebApp */;
|
|
15260
|
+
}
|
|
15462
15261
|
}
|
|
15463
15262
|
/**
|
|
15464
15263
|
* Get the mission store for a session.
|
|
@@ -15763,9 +15562,10 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
15763
15562
|
}
|
|
15764
15563
|
case "droid_working_state_changed" /* DROID_WORKING_STATE_CHANGED */: {
|
|
15765
15564
|
const { type: _, ...stateNotification } = notification;
|
|
15565
|
+
const effectiveState = this.sessionStateManager.getSessionManager(sessionId)?.getDroidWorkingState() ?? stateNotification.newState;
|
|
15766
15566
|
this.emit("droidWorkingStateChanged" /* DroidWorkingStateChanged */, {
|
|
15767
15567
|
sessionId,
|
|
15768
|
-
newState:
|
|
15568
|
+
newState: effectiveState
|
|
15769
15569
|
});
|
|
15770
15570
|
break;
|
|
15771
15571
|
}
|
|
@@ -16016,15 +15816,15 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16016
15816
|
}
|
|
16017
15817
|
logInfo("[DaemonSessionController] Clearing pending user actions", {
|
|
16018
15818
|
sessionId,
|
|
16019
|
-
|
|
16020
|
-
|
|
15819
|
+
pendingCount: pendingPermissionCount,
|
|
15820
|
+
totalQuestions: pendingAskUserCount
|
|
16021
15821
|
});
|
|
16022
15822
|
this.permissionHandler.clearSessionPermissions(sessionId, {
|
|
16023
15823
|
emitResolved: true,
|
|
16024
15824
|
preserveRelayed: options?.preserveRelayedPermissions
|
|
16025
15825
|
});
|
|
16026
15826
|
this.askUserHandler.clearSessionAskUserRequests(sessionId);
|
|
16027
|
-
this.
|
|
15827
|
+
this.sessionStateManager.clearConcurrentPrompts(sessionId);
|
|
16028
15828
|
}
|
|
16029
15829
|
getPendingPermissionCountForSession(sessionId) {
|
|
16030
15830
|
return this.permissionHandler.getPendingPermissions().filter(
|
|
@@ -16034,6 +15834,40 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16034
15834
|
hasPendingUserActionForSession(sessionId) {
|
|
16035
15835
|
return this.getPendingPermissionCountForSession(sessionId) > 0 || this.askUserHandler.getPendingAskUserRequests().some((request) => request.sessionId === sessionId);
|
|
16036
15836
|
}
|
|
15837
|
+
/**
|
|
15838
|
+
* A dead worker can be idle while its session is still waiting on a prompt
|
|
15839
|
+
* owned by this controller. Keep the composite session state waiting until
|
|
15840
|
+
* the buffered response is replayed or the prompt is explicitly cleared.
|
|
15841
|
+
*/
|
|
15842
|
+
preserveInactivePendingUserActionState(sessionId) {
|
|
15843
|
+
if (!this.sessionStateManager.isSessionInactive(sessionId) || !this.hasPendingUserActionForSession(sessionId)) {
|
|
15844
|
+
return false;
|
|
15845
|
+
}
|
|
15846
|
+
const sessionManager = this.sessionStateManager.getSessionManager(sessionId);
|
|
15847
|
+
if (sessionManager?.getDroidWorkingState() !== "waiting_for_tool_confirmation" /* WaitingForToolConfirmation */) {
|
|
15848
|
+
sessionManager?.setWaitingForConfirmation();
|
|
15849
|
+
}
|
|
15850
|
+
return true;
|
|
15851
|
+
}
|
|
15852
|
+
/**
|
|
15853
|
+
* Rewrite ONLY the inactivity-companion Idle: a paused worker reports Idle
|
|
15854
|
+
* while its session still owes a prompt owned by this controller, and that
|
|
15855
|
+
* bare Idle must not terminalize the composite state. A non-Idle
|
|
15856
|
+
* notification is a genuine wake (a respawned worker or another attached
|
|
15857
|
+
* client) and must pass through untouched — rewriting it would plant a
|
|
15858
|
+
* Waiting state whose revision bump then defeats the load hydration guards
|
|
15859
|
+
* while reconciliation clears the prompt, stranding the session in
|
|
15860
|
+
* WaitingForToolConfirmation with zero answerable prompts.
|
|
15861
|
+
*/
|
|
15862
|
+
getEffectiveStateNotification(notification, sessionId) {
|
|
15863
|
+
if (notification.type === "droid_working_state_changed" /* DROID_WORKING_STATE_CHANGED */ && notification.newState === "idle" /* Idle */ && this.sessionStateManager.isSessionInactive(sessionId) && this.hasPendingUserActionForSession(sessionId)) {
|
|
15864
|
+
return {
|
|
15865
|
+
...notification,
|
|
15866
|
+
newState: "waiting_for_tool_confirmation" /* WaitingForToolConfirmation */
|
|
15867
|
+
};
|
|
15868
|
+
}
|
|
15869
|
+
return notification;
|
|
15870
|
+
}
|
|
16037
15871
|
markConcurrentPermissionSurfaces(sessionIds) {
|
|
16038
15872
|
const pendingPermissions = this.permissionHandler.getPendingPermissions();
|
|
16039
15873
|
for (const sessionId of sessionIds) {
|
|
@@ -16045,15 +15879,15 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16045
15879
|
}
|
|
16046
15880
|
for (const permission of pendingForSession) {
|
|
16047
15881
|
for (const associatedSessionId of permission.associatedSessionIds) {
|
|
16048
|
-
this.
|
|
15882
|
+
this.sessionStateManager.markConcurrentPrompts(associatedSessionId);
|
|
16049
15883
|
}
|
|
16050
15884
|
}
|
|
16051
15885
|
}
|
|
16052
15886
|
}
|
|
16053
15887
|
clearDrainedConcurrentPromptMarkers(sessionIds) {
|
|
16054
15888
|
for (const sessionId of sessionIds) {
|
|
16055
|
-
if (this.
|
|
16056
|
-
this.
|
|
15889
|
+
if (this.sessionStateManager.hasConcurrentPrompts(sessionId) && !this.hasPendingUserActionForSession(sessionId)) {
|
|
15890
|
+
this.sessionStateManager.clearConcurrentPrompts(sessionId);
|
|
16057
15891
|
}
|
|
16058
15892
|
}
|
|
16059
15893
|
}
|
|
@@ -16071,27 +15905,27 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16071
15905
|
* draining a concurrent batch.
|
|
16072
15906
|
*
|
|
16073
15907
|
* An inactive session has NOT resumed — it paused (inactivity timeout /
|
|
16074
|
-
* process exit)
|
|
16075
|
-
*
|
|
16076
|
-
*
|
|
16077
|
-
*
|
|
16078
|
-
* reconciles prompts from the daemon
|
|
15908
|
+
* process exit). SESSION_INACTIVITY deliberately preserves outstanding
|
|
15909
|
+
* prompts "for later replay", so a raw Idle worker-state notification that
|
|
15910
|
+
* accompanies the pause must not be mistaken for a resume and clear them.
|
|
15911
|
+
* The controller keeps the effective session state waiting until the next
|
|
15912
|
+
* successful loadSession reconciles prompts from the daemon.
|
|
16079
15913
|
*/
|
|
16080
15914
|
clearStalePromptsOnResume(sessionId) {
|
|
16081
|
-
if (this.
|
|
15915
|
+
if (this.sessionStateManager.isSessionInactive(sessionId)) {
|
|
16082
15916
|
return;
|
|
16083
15917
|
}
|
|
16084
15918
|
const pendingPermissionCount = this.getPendingPermissionCountForSession(sessionId);
|
|
16085
15919
|
const pendingAskUserCount = this.askUserHandler.getPendingAskUserRequests().filter((request) => request.sessionId === sessionId).length;
|
|
16086
15920
|
if (pendingPermissionCount === 0 && pendingAskUserCount === 0) {
|
|
16087
|
-
this.
|
|
15921
|
+
this.sessionStateManager.clearConcurrentPrompts(sessionId);
|
|
16088
15922
|
return;
|
|
16089
15923
|
}
|
|
16090
15924
|
if (pendingPermissionCount > 1 || pendingAskUserCount > 1) {
|
|
16091
|
-
this.
|
|
15925
|
+
this.sessionStateManager.markConcurrentPrompts(sessionId);
|
|
16092
15926
|
return;
|
|
16093
15927
|
}
|
|
16094
|
-
if (this.
|
|
15928
|
+
if (this.sessionStateManager.hasConcurrentPrompts(sessionId)) {
|
|
16095
15929
|
return;
|
|
16096
15930
|
}
|
|
16097
15931
|
this.clearPendingUserActionsForSession(sessionId, {
|
|
@@ -16133,30 +15967,31 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16133
15967
|
}
|
|
16134
15968
|
case "session_inactivity" /* SESSION_INACTIVITY */:
|
|
16135
15969
|
case "session_process_exited" /* SESSION_PROCESS_EXITED */: {
|
|
16136
|
-
this.
|
|
15970
|
+
this.sessionStateManager.markSessionInactive(sessionId);
|
|
16137
15971
|
const pendingPermissionCount = this.permissionHandler.getPendingPermissions().filter((p) => p.sessionId === sessionId).length;
|
|
16138
15972
|
const pendingAskUserCount = this.askUserHandler.getPendingAskUserRequests().filter((r) => r.sessionId === sessionId).length;
|
|
16139
15973
|
logInfo(
|
|
16140
15974
|
"[DaemonSessionController] Session became inactive; preserving pending prompts for later replay",
|
|
16141
15975
|
{
|
|
16142
15976
|
sessionId,
|
|
16143
|
-
|
|
16144
|
-
|
|
15977
|
+
pendingCount: pendingPermissionCount,
|
|
15978
|
+
totalQuestions: pendingAskUserCount
|
|
16145
15979
|
}
|
|
16146
15980
|
);
|
|
16147
15981
|
const sessionManager = this.sessionStateManager.getSessionManager(sessionId);
|
|
16148
15982
|
if (sessionManager) {
|
|
16149
|
-
|
|
15983
|
+
const preservesPendingUserAction = this.preserveInactivePendingUserActionState(sessionId);
|
|
15984
|
+
if (!preservesPendingUserAction && sessionManager.getDroidWorkingState() !== "idle" /* Idle */) {
|
|
15985
|
+
sessionManager.applyDroidWorkingState("idle" /* Idle */);
|
|
15986
|
+
}
|
|
16150
15987
|
this.emit("droidWorkingStateChanged" /* DroidWorkingStateChanged */, {
|
|
16151
15988
|
sessionId,
|
|
16152
|
-
newState:
|
|
15989
|
+
newState: sessionManager.getDroidWorkingState()
|
|
16153
15990
|
});
|
|
16154
15991
|
}
|
|
16155
15992
|
break;
|
|
16156
15993
|
}
|
|
16157
15994
|
case "session_closed" /* SESSION_CLOSED */: {
|
|
16158
|
-
this.inactiveSessions.delete(sessionId);
|
|
16159
|
-
this.sessionLoadOptions.delete(sessionId);
|
|
16160
15995
|
this.trackedChildSessionIds.delete(sessionId);
|
|
16161
15996
|
this.childSessionAttachmentGeneration.delete(sessionId);
|
|
16162
15997
|
this.clearPendingUserActionsForSession(sessionId);
|
|
@@ -16516,10 +16351,6 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16516
16351
|
});
|
|
16517
16352
|
this.messageRouter.on("notification", ({ notification, sessionId }) => {
|
|
16518
16353
|
const associatedPermissionSessionIds = notification.type === "permission_resolved" /* PERMISSION_RESOLVED */ ? this.permissionHandler.getPendingPermission(notification.requestId)?.associatedSessionIds.filter((id) => id !== sessionId) ?? [] : [];
|
|
16519
|
-
const params = {
|
|
16520
|
-
sessionId,
|
|
16521
|
-
notification
|
|
16522
|
-
};
|
|
16523
16354
|
this.emit("sessionNotification" /* SessionNotification */, {
|
|
16524
16355
|
sessionId,
|
|
16525
16356
|
notification
|
|
@@ -16529,7 +16360,19 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16529
16360
|
notification.requestId
|
|
16530
16361
|
);
|
|
16531
16362
|
}
|
|
16532
|
-
|
|
16363
|
+
const isInactiveNotification = notification.type === "session_inactivity" /* SESSION_INACTIVITY */ || notification.type === "session_process_exited" /* SESSION_PROCESS_EXITED */;
|
|
16364
|
+
this.sessionStateManager.handleNotification(
|
|
16365
|
+
{
|
|
16366
|
+
sessionId,
|
|
16367
|
+
notification: this.getEffectiveStateNotification(
|
|
16368
|
+
notification,
|
|
16369
|
+
sessionId
|
|
16370
|
+
)
|
|
16371
|
+
},
|
|
16372
|
+
{
|
|
16373
|
+
preserveWorkingStateOnInactive: isInactiveNotification && this.hasPendingUserActionForSession(sessionId)
|
|
16374
|
+
}
|
|
16375
|
+
);
|
|
16533
16376
|
this.handleNotificationSideEffects(notification, sessionId);
|
|
16534
16377
|
this.emitDroidEventForNotification(notification, sessionId);
|
|
16535
16378
|
for (const associatedSessionId of associatedPermissionSessionIds) {
|
|
@@ -16573,7 +16416,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16573
16416
|
logInfo("[DaemonSessionController] Received permission request", {
|
|
16574
16417
|
requestId,
|
|
16575
16418
|
sessionId,
|
|
16576
|
-
|
|
16419
|
+
count: request.params.toolUses.length
|
|
16577
16420
|
});
|
|
16578
16421
|
if (!this.sessionStateManager.getSessionManager(sessionId)) {
|
|
16579
16422
|
logWarn(
|
|
@@ -16608,7 +16451,11 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16608
16451
|
this.permissionHandler.handlePermissionRequest(
|
|
16609
16452
|
requestWithAssociatedSessions,
|
|
16610
16453
|
requestId,
|
|
16611
|
-
sessionId
|
|
16454
|
+
sessionId,
|
|
16455
|
+
// Stamp the owning machine: this controller's daemon sent the
|
|
16456
|
+
// request, so ownership filtering keeps working even when the
|
|
16457
|
+
// session's manager binding is empty or later evicted.
|
|
16458
|
+
{ machineId: this.config.machineId }
|
|
16612
16459
|
);
|
|
16613
16460
|
this.markConcurrentPermissionSurfaces(
|
|
16614
16461
|
/* @__PURE__ */ new Set([sessionId, ...associatedSessionIds])
|
|
@@ -16622,7 +16469,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16622
16469
|
logInfo("[DaemonSessionController] Received ask-user request", {
|
|
16623
16470
|
requestId,
|
|
16624
16471
|
sessionId,
|
|
16625
|
-
|
|
16472
|
+
count: request.params.questions.length,
|
|
16626
16473
|
toolCallId: request.params.toolCallId
|
|
16627
16474
|
});
|
|
16628
16475
|
if (!this.sessionStateManager.getSessionManager(sessionId)) {
|
|
@@ -16638,26 +16485,68 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16638
16485
|
sessionId,
|
|
16639
16486
|
newState: "waiting_for_tool_confirmation" /* WaitingForToolConfirmation */
|
|
16640
16487
|
});
|
|
16641
|
-
this.askUserHandler.handleAskUserRequest(
|
|
16488
|
+
this.askUserHandler.handleAskUserRequest(
|
|
16489
|
+
request,
|
|
16490
|
+
requestId,
|
|
16491
|
+
sessionId,
|
|
16492
|
+
{
|
|
16493
|
+
machineId: this.config.machineId
|
|
16494
|
+
}
|
|
16495
|
+
);
|
|
16642
16496
|
void this.tryReplayBufferedAskUser(sessionId, requestId);
|
|
16643
16497
|
}
|
|
16644
16498
|
);
|
|
16645
|
-
|
|
16499
|
+
const onPermissionRequested = (permission) => {
|
|
16500
|
+
if (!this.machineOwnsPrompt(permission)) {
|
|
16501
|
+
return;
|
|
16502
|
+
}
|
|
16646
16503
|
this.emit("permissionRequested" /* PermissionRequested */, permission);
|
|
16647
16504
|
this.dispatchPermissionToSessionSubscribers(permission);
|
|
16648
|
-
}
|
|
16649
|
-
this.permissionHandler.on("
|
|
16505
|
+
};
|
|
16506
|
+
this.permissionHandler.on("permissionRequested", onPermissionRequested);
|
|
16507
|
+
this.promptStoreUnsubscribes.push(
|
|
16508
|
+
() => this.permissionHandler.off("permissionRequested", onPermissionRequested)
|
|
16509
|
+
);
|
|
16510
|
+
const onPermissionResolved = (id, option, context) => {
|
|
16511
|
+
if (context && !this.machineOwnsPrompt(context)) {
|
|
16512
|
+
return;
|
|
16513
|
+
}
|
|
16650
16514
|
this.emit("permissionResolved" /* PermissionResolved */, id, option);
|
|
16651
|
-
}
|
|
16652
|
-
this.permissionHandler.on("
|
|
16515
|
+
};
|
|
16516
|
+
this.permissionHandler.on("permissionResolved", onPermissionResolved);
|
|
16517
|
+
this.promptStoreUnsubscribes.push(
|
|
16518
|
+
() => this.permissionHandler.off("permissionResolved", onPermissionResolved)
|
|
16519
|
+
);
|
|
16520
|
+
const onPermissionTimeout = (id, context) => {
|
|
16521
|
+
if (context && !this.machineOwnsPrompt(context)) {
|
|
16522
|
+
return;
|
|
16523
|
+
}
|
|
16653
16524
|
this.emit("permissionTimeout" /* PermissionTimeout */, id);
|
|
16654
|
-
}
|
|
16655
|
-
this.
|
|
16525
|
+
};
|
|
16526
|
+
this.permissionHandler.on("permissionTimeout", onPermissionTimeout);
|
|
16527
|
+
this.promptStoreUnsubscribes.push(
|
|
16528
|
+
() => this.permissionHandler.off("permissionTimeout", onPermissionTimeout)
|
|
16529
|
+
);
|
|
16530
|
+
const onAskUserRequested = (req) => {
|
|
16531
|
+
if (!this.machineOwnsPrompt(req)) {
|
|
16532
|
+
return;
|
|
16533
|
+
}
|
|
16656
16534
|
this.emit("askUserRequested" /* AskUserRequested */, req);
|
|
16657
|
-
}
|
|
16658
|
-
this.askUserHandler.on("
|
|
16535
|
+
};
|
|
16536
|
+
this.askUserHandler.on("askUserRequested", onAskUserRequested);
|
|
16537
|
+
this.promptStoreUnsubscribes.push(
|
|
16538
|
+
() => this.askUserHandler.off("askUserRequested", onAskUserRequested)
|
|
16539
|
+
);
|
|
16540
|
+
const onAskUserResolved = (id, result, context) => {
|
|
16541
|
+
if (context && !this.machineOwnsPrompt(context)) {
|
|
16542
|
+
return;
|
|
16543
|
+
}
|
|
16659
16544
|
this.emit("askUserResolved" /* AskUserResolved */, id, result);
|
|
16660
|
-
}
|
|
16545
|
+
};
|
|
16546
|
+
this.askUserHandler.on("askUserResolved", onAskUserResolved);
|
|
16547
|
+
this.promptStoreUnsubscribes.push(
|
|
16548
|
+
() => this.askUserHandler.off("askUserResolved", onAskUserResolved)
|
|
16549
|
+
);
|
|
16661
16550
|
}
|
|
16662
16551
|
handleWebSocketMessage(data) {
|
|
16663
16552
|
try {
|
|
@@ -16931,7 +16820,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16931
16820
|
token,
|
|
16932
16821
|
...options?.actAsGrant && { actAsGrant: options.actAsGrant },
|
|
16933
16822
|
...connectionId && { connectionId },
|
|
16934
|
-
caller: this.
|
|
16823
|
+
caller: this.getFactoryClientType(),
|
|
16935
16824
|
metadata: {
|
|
16936
16825
|
tracing: {
|
|
16937
16826
|
...this.daemonClient.getTracingMetadata(),
|
|
@@ -16940,7 +16829,8 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16940
16829
|
connectAttemptId: this.connectAttemptContext.connectAttemptId,
|
|
16941
16830
|
connectAttemptTrigger: this.connectAttemptContext.connectAttemptTrigger
|
|
16942
16831
|
}
|
|
16943
|
-
}
|
|
16832
|
+
},
|
|
16833
|
+
...this.config.sdk && { sdk: this.config.sdk }
|
|
16944
16834
|
}
|
|
16945
16835
|
});
|
|
16946
16836
|
try {
|
|
@@ -16962,7 +16852,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16962
16852
|
await this.daemonClient.logout();
|
|
16963
16853
|
this._isAuthenticated = false;
|
|
16964
16854
|
this.authenticationPromise = null;
|
|
16965
|
-
this.
|
|
16855
|
+
this.clearPromptsForOwnedSessions();
|
|
16966
16856
|
this.emit("connectionStatusChanged" /* StatusChanged */, this.getConnectionStatus());
|
|
16967
16857
|
}
|
|
16968
16858
|
disconnect() {
|
|
@@ -16977,10 +16867,33 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
16977
16867
|
this.isReconnecting = false;
|
|
16978
16868
|
this._isAuthenticated = false;
|
|
16979
16869
|
this.authenticationPromise = null;
|
|
16980
|
-
this.permissionHandler.clearAllPermissions();
|
|
16981
16870
|
this.daemonClient.destroy();
|
|
16982
16871
|
this.emit("connectionStatusChanged" /* StatusChanged */, this.getConnectionStatus());
|
|
16983
16872
|
}
|
|
16873
|
+
/**
|
|
16874
|
+
* Clear pending prompts owned by this controller's machine, leaving other
|
|
16875
|
+
* machines' prompt state in the shared stores untouched.
|
|
16876
|
+
*
|
|
16877
|
+
* Ownership resolves from the machineId stamped on the record at intake,
|
|
16878
|
+
* falling back to the session's manager binding when absent — the same
|
|
16879
|
+
* identity the emission predicate uses, so a prompt this machine surfaced
|
|
16880
|
+
* (including one taken in while its session was still `''`-bound optimistic
|
|
16881
|
+
* or whose manager was since evicted) is cleared by this machine's logout.
|
|
16882
|
+
*/
|
|
16883
|
+
clearPromptsForOwnedSessions() {
|
|
16884
|
+
for (const permission of this.permissionHandler.getPendingPermissions()) {
|
|
16885
|
+
if (this.getPromptOwnership(permission) === "owned") {
|
|
16886
|
+
this.permissionHandler.clearPermission(permission.requestId, {
|
|
16887
|
+
emitResolved: true
|
|
16888
|
+
});
|
|
16889
|
+
}
|
|
16890
|
+
}
|
|
16891
|
+
for (const request of this.askUserHandler.getPendingAskUserRequests()) {
|
|
16892
|
+
if (this.getPromptOwnership(request) === "owned") {
|
|
16893
|
+
this.askUserHandler.clearAskUser(request.requestId);
|
|
16894
|
+
}
|
|
16895
|
+
}
|
|
16896
|
+
}
|
|
16984
16897
|
/**
|
|
16985
16898
|
* Send `initialize_session` to the daemon, transparently retrying on
|
|
16986
16899
|
* RequestTimeoutError up to INITIALIZE_SESSION_MAX_ATTEMPTS. Other errors
|
|
@@ -17072,7 +16985,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17072
16985
|
if (callingToolUseId) {
|
|
17073
16986
|
sessionManager?.getStore().setCallingToolUseId(callingToolUseId);
|
|
17074
16987
|
}
|
|
17075
|
-
this.
|
|
16988
|
+
this.sessionStateManager.setSessionLoadOptions(
|
|
17076
16989
|
result.sessionId,
|
|
17077
16990
|
DaemonLoadSessionSpawnOptionsSchema.parse({
|
|
17078
16991
|
disableInactivityTimeout: params.disableInactivityTimeout,
|
|
@@ -17168,7 +17081,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17168
17081
|
if (loadState !== "NOT_LOADED" /* NotLoaded */) {
|
|
17169
17082
|
return;
|
|
17170
17083
|
}
|
|
17171
|
-
const stashed = this.
|
|
17084
|
+
const stashed = this.sessionStateManager.getSessionLoadOptions(sessionId) ?? {};
|
|
17172
17085
|
await this.loadSession({
|
|
17173
17086
|
...stashed,
|
|
17174
17087
|
sessionId,
|
|
@@ -17204,7 +17117,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17204
17117
|
if (loadState === "LOADING" /* Loading */) {
|
|
17205
17118
|
return;
|
|
17206
17119
|
}
|
|
17207
|
-
const stashed = this.
|
|
17120
|
+
const stashed = this.sessionStateManager.getSessionLoadOptions(sessionId) ?? {};
|
|
17208
17121
|
try {
|
|
17209
17122
|
await this.loadSession({
|
|
17210
17123
|
...stashed,
|
|
@@ -17261,15 +17174,16 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17261
17174
|
params.sessionId,
|
|
17262
17175
|
this.config.machineId
|
|
17263
17176
|
);
|
|
17177
|
+
const workingStateRevisionBeforeLoad = this.sessionStateManager.getSessionManager(params.sessionId)?.getWorkingStateRevision() ?? 0;
|
|
17264
17178
|
const fetched = await this.config.getAccessToken();
|
|
17265
17179
|
if (fetched === null) {
|
|
17266
17180
|
throw new MetaError("No access token available for loadSession", {
|
|
17267
17181
|
sessionId: params.sessionId
|
|
17268
17182
|
});
|
|
17269
17183
|
}
|
|
17270
|
-
const
|
|
17184
|
+
const stashedOptions = this.sessionStateManager.getSessionLoadOptions(params.sessionId) ?? {};
|
|
17271
17185
|
const structuredOutputFormat = resolveStructuredOutputFormat(
|
|
17272
|
-
|
|
17186
|
+
stashedOptions,
|
|
17273
17187
|
params
|
|
17274
17188
|
);
|
|
17275
17189
|
const resolvedParams = {
|
|
@@ -17278,9 +17192,12 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17278
17192
|
token: fetched,
|
|
17279
17193
|
loadAllMessages: this.getClientType() === "cli" ? params.loadAllMessages : true,
|
|
17280
17194
|
messageLimit: params.messageLimit ?? this.config.defaultMessageLimit,
|
|
17281
|
-
disableInactivityTimeout: params.disableInactivityTimeout ?? this.config.disableInactivityTimeout
|
|
17195
|
+
disableInactivityTimeout: params.disableInactivityTimeout ?? stashedOptions.disableInactivityTimeout ?? this.config.disableInactivityTimeout,
|
|
17196
|
+
disableBuiltinSkills: params.disableBuiltinSkills ?? stashedOptions.disableBuiltinSkills,
|
|
17197
|
+
skipPermissionsUnsafe: params.skipPermissionsUnsafe ?? stashedOptions.skipPermissionsUnsafe,
|
|
17198
|
+
runtimeSettingsPath: params.runtimeSettingsPath ?? stashedOptions.runtimeSettingsPath
|
|
17282
17199
|
};
|
|
17283
|
-
this.
|
|
17200
|
+
this.sessionStateManager.setSessionLoadOptions(
|
|
17284
17201
|
params.sessionId,
|
|
17285
17202
|
DaemonLoadSessionSpawnOptionsSchema.parse({
|
|
17286
17203
|
disableInactivityTimeout: resolvedParams.disableInactivityTimeout,
|
|
@@ -17291,10 +17208,18 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17291
17208
|
})
|
|
17292
17209
|
);
|
|
17293
17210
|
let result;
|
|
17211
|
+
let reportedWorkingState;
|
|
17212
|
+
let didHydrateReportedWorkingState = false;
|
|
17294
17213
|
const loadPromise = this.daemonClient.loadSession(resolvedParams);
|
|
17295
17214
|
this.daemonClient.setPendingSessionReady?.(params.sessionId, loadPromise);
|
|
17296
17215
|
try {
|
|
17297
17216
|
result = await loadPromise;
|
|
17217
|
+
reportedWorkingState = result.workingState !== void 0 && result.workingState !== "idle" /* Idle */ ? result.workingState : result.isAgentLoopInProgress ? "streaming_assistant_message" /* StreamingAssistantMessage */ : "idle" /* Idle */;
|
|
17218
|
+
const sessionManager2 = this.sessionStateManager.getSessionManager(
|
|
17219
|
+
params.sessionId
|
|
17220
|
+
);
|
|
17221
|
+
const shouldHydrateReportedWorkingState = sessionManager2?.getWorkingStateRevision() === workingStateRevisionBeforeLoad;
|
|
17222
|
+
didHydrateReportedWorkingState = shouldHydrateReportedWorkingState && sessionManager2?.getDroidWorkingState() !== reportedWorkingState;
|
|
17298
17223
|
this.sessionStateManager.loadSession(
|
|
17299
17224
|
params.sessionId,
|
|
17300
17225
|
this.config.machineId,
|
|
@@ -17302,7 +17227,8 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17302
17227
|
{
|
|
17303
17228
|
cwd: result.cwd ?? null,
|
|
17304
17229
|
hasOlderMessages: result.hasOlderMessages ?? false,
|
|
17305
|
-
loadedMessageLimit: resolvedParams.messageLimit ?? 0
|
|
17230
|
+
loadedMessageLimit: resolvedParams.messageLimit ?? 0,
|
|
17231
|
+
...shouldHydrateReportedWorkingState ? { workingState: reportedWorkingState } : {}
|
|
17306
17232
|
}
|
|
17307
17233
|
);
|
|
17308
17234
|
if (result.subagentInvocations && result.subagentInvocations.length > 0) {
|
|
@@ -17325,7 +17251,6 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17325
17251
|
const sessionManager = this.sessionStateManager.getSessionManager(
|
|
17326
17252
|
params.sessionId
|
|
17327
17253
|
);
|
|
17328
|
-
const workingStateRevisionAtLoad = sessionManager?.getWorkingStateRevision() ?? 0;
|
|
17329
17254
|
if (sessionManager && result.settings) {
|
|
17330
17255
|
if (result.settings.modelId !== void 0) {
|
|
17331
17256
|
sessionManager.getStore().setModelId(result.settings.modelId);
|
|
@@ -17427,7 +17352,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17427
17352
|
request.data,
|
|
17428
17353
|
permissionRequest.requestId,
|
|
17429
17354
|
params.sessionId,
|
|
17430
|
-
{ isRestored: true }
|
|
17355
|
+
{ isRestored: true, machineId: this.config.machineId }
|
|
17431
17356
|
);
|
|
17432
17357
|
surfacedPermissionCount += 1;
|
|
17433
17358
|
}
|
|
@@ -17501,7 +17426,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17501
17426
|
request.data,
|
|
17502
17427
|
askUserRequest.requestId,
|
|
17503
17428
|
params.sessionId,
|
|
17504
|
-
{ isRestored: true }
|
|
17429
|
+
{ isRestored: true, machineId: this.config.machineId }
|
|
17505
17430
|
);
|
|
17506
17431
|
}
|
|
17507
17432
|
logInfo(
|
|
@@ -17517,7 +17442,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17517
17442
|
);
|
|
17518
17443
|
}
|
|
17519
17444
|
} finally {
|
|
17520
|
-
this.
|
|
17445
|
+
this.sessionStateManager.markSessionActive(params.sessionId);
|
|
17521
17446
|
}
|
|
17522
17447
|
if (sessionManager) {
|
|
17523
17448
|
await reconcileQueuedMessagesAfterLoad({
|
|
@@ -17528,9 +17453,8 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17528
17453
|
});
|
|
17529
17454
|
}
|
|
17530
17455
|
const hasAssociatedPendingPermission = this.permissionHandler.getPendingPermissionsForSession(params.sessionId).length > 0;
|
|
17531
|
-
const reportedWorkingState = result.workingState !== void 0 && result.workingState !== "idle" /* Idle */ ? result.workingState : result.isAgentLoopInProgress ? "streaming_assistant_message" /* StreamingAssistantMessage */ : "idle" /* Idle */;
|
|
17532
17456
|
const loadedWorkingState = hasAssociatedPendingPermission || hasPendingAskUserRequests ? "waiting_for_tool_confirmation" /* WaitingForToolConfirmation */ : reportedWorkingState;
|
|
17533
|
-
if (sessionManager && sessionManager.getWorkingStateRevision() !==
|
|
17457
|
+
if (sessionManager && sessionManager.getWorkingStateRevision() !== workingStateRevisionBeforeLoad) {
|
|
17534
17458
|
logInfo(
|
|
17535
17459
|
"[DaemonSessionController] Skipping stale working state from load",
|
|
17536
17460
|
{
|
|
@@ -17539,18 +17463,23 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17539
17463
|
state: sessionManager.getDroidWorkingState()
|
|
17540
17464
|
}
|
|
17541
17465
|
);
|
|
17542
|
-
} else if (
|
|
17543
|
-
sessionManager
|
|
17544
|
-
|
|
17545
|
-
|
|
17546
|
-
|
|
17547
|
-
|
|
17548
|
-
|
|
17549
|
-
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17466
|
+
} else if (sessionManager) {
|
|
17467
|
+
const didApplyLoadedWorkingState = sessionManager.getDroidWorkingState() !== loadedWorkingState;
|
|
17468
|
+
if (didApplyLoadedWorkingState) {
|
|
17469
|
+
sessionManager.applyDroidWorkingState(loadedWorkingState);
|
|
17470
|
+
}
|
|
17471
|
+
if (loadedWorkingState !== "idle" /* Idle */ || didHydrateReportedWorkingState || didApplyLoadedWorkingState) {
|
|
17472
|
+
this.emit("droidWorkingStateChanged" /* DroidWorkingStateChanged */, {
|
|
17473
|
+
sessionId: params.sessionId,
|
|
17474
|
+
newState: loadedWorkingState
|
|
17475
|
+
});
|
|
17476
|
+
logInfo("[DaemonSessionController] Restored working state on load", {
|
|
17477
|
+
sessionId: params.sessionId,
|
|
17478
|
+
value: loadedWorkingState,
|
|
17479
|
+
pendingCount: hasAssociatedPendingPermission ? 1 : 0,
|
|
17480
|
+
totalQuestions: hasPendingAskUserRequests ? 1 : 0
|
|
17481
|
+
});
|
|
17482
|
+
}
|
|
17554
17483
|
}
|
|
17555
17484
|
if (this.config.supportsTerminalRestoreOnLoad !== false) {
|
|
17556
17485
|
await this.loadTerminals(params.sessionId);
|
|
@@ -17634,6 +17563,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17634
17563
|
const wasOptimisticallySubmittedWhileIdleBeforeRequest = sessionManager?.getDroidWorkingState() === "idle" /* Idle */ && sessionManager.getOptimisticMessage(requestId) !== void 0;
|
|
17635
17564
|
const content = buildUserMessageContentBlocks2({
|
|
17636
17565
|
text: params.text,
|
|
17566
|
+
content: params.content,
|
|
17637
17567
|
images: params.images,
|
|
17638
17568
|
files: params.files
|
|
17639
17569
|
});
|
|
@@ -17754,15 +17684,9 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17754
17684
|
const result = await this.daemonClient.interruptSession({
|
|
17755
17685
|
sessionId
|
|
17756
17686
|
});
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
this.emit(
|
|
17761
|
-
"permissionResolved" /* PermissionResolved */,
|
|
17762
|
-
permission.requestId,
|
|
17763
|
-
"cancel" /* Cancel */
|
|
17764
|
-
);
|
|
17765
|
-
}
|
|
17687
|
+
this.permissionHandler.clearSessionPermissions(sessionId, {
|
|
17688
|
+
emitResolved: true
|
|
17689
|
+
});
|
|
17766
17690
|
const askUserToClose = this.askUserHandler.getPendingAskUserRequests().filter((r) => r.sessionId === sessionId);
|
|
17767
17691
|
for (const req of askUserToClose) {
|
|
17768
17692
|
try {
|
|
@@ -17806,7 +17730,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17806
17730
|
*/
|
|
17807
17731
|
async closeSession(sessionId) {
|
|
17808
17732
|
await this.daemonClient.closeSession({ sessionId });
|
|
17809
|
-
this.
|
|
17733
|
+
this.sessionStateManager.deleteSessionLoadOptions(sessionId);
|
|
17810
17734
|
this.sessionLoadInFlight.delete(sessionId);
|
|
17811
17735
|
this.trackedChildSessionIds.delete(sessionId);
|
|
17812
17736
|
this.childSessionAttachmentGeneration.delete(sessionId);
|
|
@@ -17876,8 +17800,11 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
17876
17800
|
async resolvePullRequestStatuses(params) {
|
|
17877
17801
|
return await this.daemonClient.resolvePullRequestStatuses(params);
|
|
17878
17802
|
}
|
|
17879
|
-
async listGitBranches(cwd) {
|
|
17880
|
-
return this.daemonClient.listGitBranches({
|
|
17803
|
+
async listGitBranches(cwd, options) {
|
|
17804
|
+
return this.daemonClient.listGitBranches({
|
|
17805
|
+
cwd,
|
|
17806
|
+
...options?.includeOriginBranches === true ? { includeOriginBranches: true } : {}
|
|
17807
|
+
});
|
|
17881
17808
|
}
|
|
17882
17809
|
async checkoutGitBranch(params) {
|
|
17883
17810
|
return this.daemonClient.checkoutGitBranch(params);
|
|
@@ -18445,7 +18372,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
18445
18372
|
async runSelfResumeWithRetry(sessionId) {
|
|
18446
18373
|
let lastError;
|
|
18447
18374
|
for (let attempt = 1; attempt <= SELF_RESUME_MAX_ATTEMPTS; attempt++) {
|
|
18448
|
-
const stashed = this.
|
|
18375
|
+
const stashed = this.sessionStateManager.getSessionLoadOptions(sessionId) ?? {};
|
|
18449
18376
|
try {
|
|
18450
18377
|
await this.loadSession({
|
|
18451
18378
|
sessionId,
|
|
@@ -18534,7 +18461,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
18534
18461
|
}
|
|
18535
18462
|
);
|
|
18536
18463
|
}
|
|
18537
|
-
if (this.
|
|
18464
|
+
if (this.sessionStateManager.isSessionInactive(executionSessionId)) {
|
|
18538
18465
|
const toolUseId = permission.toolUses[0]?.toolUse.id;
|
|
18539
18466
|
if (toolUseId) {
|
|
18540
18467
|
this.pendingUserActionStore.savePermission({
|
|
@@ -18593,7 +18520,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
18593
18520
|
requestId
|
|
18594
18521
|
});
|
|
18595
18522
|
}
|
|
18596
|
-
if (this.
|
|
18523
|
+
if (this.sessionStateManager.isSessionInactive(sessionId)) {
|
|
18597
18524
|
this.pendingUserActionStore.saveAskUser({
|
|
18598
18525
|
kind: "askUser",
|
|
18599
18526
|
sessionId,
|
|
@@ -18718,6 +18645,46 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
18718
18645
|
getMachineId() {
|
|
18719
18646
|
return this.config.machineId;
|
|
18720
18647
|
}
|
|
18648
|
+
/**
|
|
18649
|
+
* Whether a prompt (or its resolution) involves this controller's machine.
|
|
18650
|
+
*
|
|
18651
|
+
* A prompt is owned by this machine when its stamped `machineId` (set at
|
|
18652
|
+
* intake by the controller that received it) matches, or when any involved
|
|
18653
|
+
* session's manager is bound to this machine (a relayed subagent prompt
|
|
18654
|
+
* surfaces on the parent's machine via `associatedSessionIds`). Sessions
|
|
18655
|
+
* with no registered manager (or no machine binding yet) don't count either
|
|
18656
|
+
* way; if the prompt carries no stamp and none of the sessions has a known
|
|
18657
|
+
* binding, this returns true so pre-registration edge cases keep the
|
|
18658
|
+
* pre-shared-store behavior of emitting.
|
|
18659
|
+
*
|
|
18660
|
+
* Public so the CLI's TuiDaemonAdapter can dedupe the legacy remote relay
|
|
18661
|
+
* against this controller's native shared-store fan-out.
|
|
18662
|
+
*/
|
|
18663
|
+
machineOwnsPrompt(prompt) {
|
|
18664
|
+
const sessionIds = /* @__PURE__ */ new Set([
|
|
18665
|
+
prompt.sessionId,
|
|
18666
|
+
...prompt.associatedSessionIds ?? []
|
|
18667
|
+
]);
|
|
18668
|
+
const ownerships = [...sessionIds].map(
|
|
18669
|
+
(sessionId) => this.getPromptOwnership({
|
|
18670
|
+
sessionId,
|
|
18671
|
+
machineId: sessionId === prompt.sessionId ? prompt.machineId : void 0
|
|
18672
|
+
})
|
|
18673
|
+
);
|
|
18674
|
+
return ownerships.includes("owned") || ownerships.every((ownership) => ownership === "unknown");
|
|
18675
|
+
}
|
|
18676
|
+
/**
|
|
18677
|
+
* Resolve one prompt record or involved session against this controller.
|
|
18678
|
+
* Emission treats an all-unknown prompt as visible. Logout clears only an
|
|
18679
|
+
* exact owned record.
|
|
18680
|
+
*/
|
|
18681
|
+
getPromptOwnership(prompt) {
|
|
18682
|
+
const machineId = prompt.machineId || this.sessionStateManager.getSessionManager(prompt.sessionId)?.getMachineId();
|
|
18683
|
+
if (!machineId) {
|
|
18684
|
+
return "unknown";
|
|
18685
|
+
}
|
|
18686
|
+
return machineId === this.config.machineId ? "owned" : "foreign";
|
|
18687
|
+
}
|
|
18721
18688
|
/**
|
|
18722
18689
|
* Get the machine type for this daemon client.
|
|
18723
18690
|
*/
|
|
@@ -18822,8 +18789,10 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
18822
18789
|
this.disconnect();
|
|
18823
18790
|
this.removeAllListeners();
|
|
18824
18791
|
this.messageRouter.removeAllListeners();
|
|
18825
|
-
this.
|
|
18826
|
-
|
|
18792
|
+
for (const unsubscribe of this.promptStoreUnsubscribes) {
|
|
18793
|
+
unsubscribe();
|
|
18794
|
+
}
|
|
18795
|
+
this.promptStoreUnsubscribes.length = 0;
|
|
18827
18796
|
this.sessionPermissionSubscribers.clear();
|
|
18828
18797
|
this.sessionLoadInFlight.clear();
|
|
18829
18798
|
this.trackedChildSessionIds.clear();
|
|
@@ -18832,7 +18801,7 @@ var DaemonSessionController = class _DaemonSessionController extends EventEmitte
|
|
|
18832
18801
|
};
|
|
18833
18802
|
|
|
18834
18803
|
// ../daemon-sdk/src/session/ControllerEventAggregator.ts
|
|
18835
|
-
import { EventEmitter as
|
|
18804
|
+
import { EventEmitter as EventEmitter5 } from "eventemitter3";
|
|
18836
18805
|
|
|
18837
18806
|
// ../daemon-sdk/src/session/mcpStatus.ts
|
|
18838
18807
|
function deriveMcpStatusFromSummary(summary) {
|
|
@@ -19131,7 +19100,7 @@ var TOOL_DISPLAY_NAMES = {
|
|
|
19131
19100
|
[TOOL_LLM_ID_TODO_WRITE]: "Plan",
|
|
19132
19101
|
[TOOL_LLM_ID_ASK_USER]: "Ask User",
|
|
19133
19102
|
[TOOL_LLM_ID_WEB_SEARCH]: "Web Search",
|
|
19134
|
-
[TOOL_LLM_ID_FETCH_URL]: "Fetch
|
|
19103
|
+
[TOOL_LLM_ID_FETCH_URL]: "Web Fetch",
|
|
19135
19104
|
[TOOL_LLM_ID_GENERATE_IMAGE]: "Generate Image",
|
|
19136
19105
|
[TOOL_LLM_ID_GET_IDE_DIAGNOSTICS]: "Get IDE Diagnostics",
|
|
19137
19106
|
[TOOL_LLM_ID_EXIT_SPEC_MODE]: "Propose Specification",
|
|
@@ -19140,7 +19109,7 @@ var TOOL_DISPLAY_NAMES = {
|
|
|
19140
19109
|
[TOOL_LLM_ID_SELECT_FEATURE]: "Select Feature",
|
|
19141
19110
|
[TOOL_LLM_ID_END_FEATURE_RUN]: "End Feature Run",
|
|
19142
19111
|
[TOOL_LLM_ID_DISMISS_HANDOFF_ITEMS]: "Dismiss Handoff Items",
|
|
19143
|
-
[TOOL_LLM_ID_TASK]: "
|
|
19112
|
+
[TOOL_LLM_ID_TASK]: "Task",
|
|
19144
19113
|
[TOOL_LLM_ID_CONNECTOR_SEARCH]: "Connectors",
|
|
19145
19114
|
[TOOL_LLM_ID_CONNECT_APP]: "Connect App"
|
|
19146
19115
|
};
|
|
@@ -20967,7 +20936,7 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
20967
20936
|
}
|
|
20968
20937
|
return void 0;
|
|
20969
20938
|
}
|
|
20970
|
-
completeStreamingContentBlocks() {
|
|
20939
|
+
completeStreamingContentBlocks(options) {
|
|
20971
20940
|
for (const message of this.store.getMessages()) {
|
|
20972
20941
|
let didUpdateMessage = false;
|
|
20973
20942
|
let thinkingBlockOrdinal = 0;
|
|
@@ -21001,10 +20970,14 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
21001
20970
|
return block;
|
|
21002
20971
|
});
|
|
21003
20972
|
if (didUpdateMessage) {
|
|
21004
|
-
this.store.updateMessage(
|
|
21005
|
-
|
|
21006
|
-
|
|
21007
|
-
|
|
20973
|
+
this.store.updateMessage(
|
|
20974
|
+
message.id,
|
|
20975
|
+
{
|
|
20976
|
+
content,
|
|
20977
|
+
updatedAt: Date.now()
|
|
20978
|
+
},
|
|
20979
|
+
options
|
|
20980
|
+
);
|
|
21008
20981
|
}
|
|
21009
20982
|
}
|
|
21010
20983
|
this.streamingMessageIds.clear();
|
|
@@ -21259,9 +21232,9 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
21259
21232
|
/**
|
|
21260
21233
|
* Set the load state of this session.
|
|
21261
21234
|
*/
|
|
21262
|
-
setLoadState(state) {
|
|
21235
|
+
setLoadState(state, options) {
|
|
21263
21236
|
this.loadState = state;
|
|
21264
|
-
if (state === "NOT_LOADED" /* NotLoaded */ && this.droidWorkingState !== "idle" /* Idle */) {
|
|
21237
|
+
if (state === "NOT_LOADED" /* NotLoaded */ && options?.preserveWorkingState !== true && this.droidWorkingState !== "idle" /* Idle */) {
|
|
21265
21238
|
this.stopStreaming();
|
|
21266
21239
|
}
|
|
21267
21240
|
}
|
|
@@ -21877,7 +21850,7 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
21877
21850
|
*
|
|
21878
21851
|
* @param params - The notification parameters including sessionId and notification data
|
|
21879
21852
|
*/
|
|
21880
|
-
handleNotification(params) {
|
|
21853
|
+
handleNotification(params, options) {
|
|
21881
21854
|
const { notification } = params;
|
|
21882
21855
|
const isStreamingDelta = notification.type === "assistant_text_delta" /* ASSISTANT_TEXT_DELTA */ || notification.type === "thinking_text_delta" /* THINKING_TEXT_DELTA */;
|
|
21883
21856
|
const isBlockComplete = notification.type === "assistant_text_complete" /* ASSISTANT_TEXT_COMPLETE */ || notification.type === "thinking_text_complete" /* THINKING_TEXT_COMPLETE */;
|
|
@@ -21961,8 +21934,7 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
21961
21934
|
sessionId: params.sessionId,
|
|
21962
21935
|
type: notification.errorType,
|
|
21963
21936
|
errorName: notification.error?.name,
|
|
21964
|
-
message: notification.message
|
|
21965
|
-
cause: notification.error
|
|
21937
|
+
message: notification.message
|
|
21966
21938
|
});
|
|
21967
21939
|
}
|
|
21968
21940
|
this.stopStreaming();
|
|
@@ -21975,7 +21947,9 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
21975
21947
|
timestamp: notification.timestamp,
|
|
21976
21948
|
timeout: notification.timeoutSeconds
|
|
21977
21949
|
});
|
|
21978
|
-
this.setLoadState("NOT_LOADED" /* NotLoaded
|
|
21950
|
+
this.setLoadState("NOT_LOADED" /* NotLoaded */, {
|
|
21951
|
+
preserveWorkingState: options?.preserveWorkingStateOnInactive === true
|
|
21952
|
+
});
|
|
21979
21953
|
break;
|
|
21980
21954
|
}
|
|
21981
21955
|
case "session_process_exited" /* SESSION_PROCESS_EXITED */: {
|
|
@@ -21984,7 +21958,9 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
21984
21958
|
message: notification.message,
|
|
21985
21959
|
timestamp: notification.timestamp
|
|
21986
21960
|
});
|
|
21987
|
-
this.setLoadState("NOT_LOADED" /* NotLoaded
|
|
21961
|
+
this.setLoadState("NOT_LOADED" /* NotLoaded */, {
|
|
21962
|
+
preserveWorkingState: options?.preserveWorkingStateOnInactive === true
|
|
21963
|
+
});
|
|
21988
21964
|
break;
|
|
21989
21965
|
}
|
|
21990
21966
|
case "session_closed" /* SESSION_CLOSED */: {
|
|
@@ -22384,68 +22360,76 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
22384
22360
|
this.droidWorkingState = newState;
|
|
22385
22361
|
this.droidWorkingStateChangedAtMs = Date.now();
|
|
22386
22362
|
}
|
|
22363
|
+
transitionDroidWorkingState(newState, options) {
|
|
22364
|
+
const previousState = this.droidWorkingState;
|
|
22365
|
+
const transition = () => {
|
|
22366
|
+
if (newState !== "idle" /* Idle */) {
|
|
22367
|
+
this.store.clearAgentTurnCompletionReason();
|
|
22368
|
+
}
|
|
22369
|
+
switch (newState) {
|
|
22370
|
+
case "idle" /* Idle */:
|
|
22371
|
+
case "executing_tool" /* ExecutingTool */:
|
|
22372
|
+
case "waiting_for_tool_confirmation" /* WaitingForToolConfirmation */:
|
|
22373
|
+
this.completeStreamingContentBlocks({
|
|
22374
|
+
silent: options?.silent === true
|
|
22375
|
+
});
|
|
22376
|
+
break;
|
|
22377
|
+
case "thinking" /* Thinking */:
|
|
22378
|
+
case "streaming_assistant_message" /* StreamingAssistantMessage */:
|
|
22379
|
+
case "compacting_conversation" /* CompactingConversation */:
|
|
22380
|
+
break;
|
|
22381
|
+
default: {
|
|
22382
|
+
const exhaustiveCheck = newState;
|
|
22383
|
+
logError("Unknown droid working state:", { value: exhaustiveCheck });
|
|
22384
|
+
return;
|
|
22385
|
+
}
|
|
22386
|
+
}
|
|
22387
|
+
this.updateDroidWorkingState(newState);
|
|
22388
|
+
};
|
|
22389
|
+
if (options?.silent === true) {
|
|
22390
|
+
this.store.runWithStateEventsSuppressed(transition);
|
|
22391
|
+
} else {
|
|
22392
|
+
transition();
|
|
22393
|
+
}
|
|
22394
|
+
const didChange = previousState !== this.droidWorkingState;
|
|
22395
|
+
if (didChange && options?.silent !== true) {
|
|
22396
|
+
this.store.flushNotify("working_state_changed" /* WorkingStateChanged */);
|
|
22397
|
+
}
|
|
22398
|
+
}
|
|
22387
22399
|
startStreaming() {
|
|
22388
|
-
this.
|
|
22389
|
-
|
|
22390
|
-
|
|
22400
|
+
this.transitionDroidWorkingState(
|
|
22401
|
+
"streaming_assistant_message" /* StreamingAssistantMessage */
|
|
22402
|
+
);
|
|
22391
22403
|
}
|
|
22392
22404
|
stopStreaming() {
|
|
22393
|
-
this.
|
|
22394
|
-
this.completeStreamingContentBlocks();
|
|
22395
|
-
this.store.flushNotify("working_state_changed" /* WorkingStateChanged */);
|
|
22405
|
+
this.transitionDroidWorkingState("idle" /* Idle */);
|
|
22396
22406
|
}
|
|
22397
22407
|
setWaitingForConfirmation() {
|
|
22398
|
-
this.
|
|
22399
|
-
|
|
22400
|
-
|
|
22408
|
+
this.transitionDroidWorkingState(
|
|
22409
|
+
"waiting_for_tool_confirmation" /* WaitingForToolConfirmation */
|
|
22410
|
+
);
|
|
22401
22411
|
}
|
|
22402
22412
|
setExecutingTool() {
|
|
22403
|
-
this.
|
|
22404
|
-
this.updateDroidWorkingState("executing_tool" /* ExecutingTool */);
|
|
22405
|
-
this.store.flushNotify("working_state_changed" /* WorkingStateChanged */);
|
|
22413
|
+
this.transitionDroidWorkingState("executing_tool" /* ExecutingTool */);
|
|
22406
22414
|
}
|
|
22407
22415
|
setCompacting() {
|
|
22408
|
-
this.
|
|
22409
|
-
this.store.flushNotify("working_state_changed" /* WorkingStateChanged */);
|
|
22416
|
+
this.transitionDroidWorkingState("compacting_conversation" /* CompactingConversation */);
|
|
22410
22417
|
}
|
|
22411
22418
|
setThinking() {
|
|
22412
|
-
this.
|
|
22413
|
-
this.store.flushNotify("working_state_changed" /* WorkingStateChanged */);
|
|
22419
|
+
this.transitionDroidWorkingState("thinking" /* Thinking */);
|
|
22414
22420
|
}
|
|
22415
22421
|
/**
|
|
22416
|
-
* Apply a DroidWorkingState value
|
|
22417
|
-
* matching setter. Used both by the working-state notification handler and by
|
|
22418
|
-
* session load, where the load result reports the current state of an
|
|
22419
|
-
* already-running session.
|
|
22422
|
+
* Apply a live DroidWorkingState value and notify subscribers when it changes.
|
|
22420
22423
|
*/
|
|
22421
22424
|
applyDroidWorkingState(newState) {
|
|
22422
|
-
|
|
22423
|
-
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
this.setThinking();
|
|
22431
|
-
break;
|
|
22432
|
-
case "streaming_assistant_message" /* StreamingAssistantMessage */:
|
|
22433
|
-
this.startStreaming();
|
|
22434
|
-
break;
|
|
22435
|
-
case "executing_tool" /* ExecutingTool */:
|
|
22436
|
-
this.setExecutingTool();
|
|
22437
|
-
break;
|
|
22438
|
-
case "waiting_for_tool_confirmation" /* WaitingForToolConfirmation */:
|
|
22439
|
-
this.setWaitingForConfirmation();
|
|
22440
|
-
break;
|
|
22441
|
-
case "compacting_conversation" /* CompactingConversation */:
|
|
22442
|
-
this.setCompacting();
|
|
22443
|
-
break;
|
|
22444
|
-
default: {
|
|
22445
|
-
const exhaustiveCheck = newState;
|
|
22446
|
-
logError("Unknown droid working state:", { value: exhaustiveCheck });
|
|
22447
|
-
}
|
|
22448
|
-
}
|
|
22425
|
+
this.transitionDroidWorkingState(newState);
|
|
22426
|
+
}
|
|
22427
|
+
/**
|
|
22428
|
+
* Hydrate a load-response working state before message publication without
|
|
22429
|
+
* exposing an intermediate transcript/state pair to subscribers.
|
|
22430
|
+
*/
|
|
22431
|
+
hydrateDroidWorkingState(newState) {
|
|
22432
|
+
this.transitionDroidWorkingState(newState, { silent: true });
|
|
22449
22433
|
}
|
|
22450
22434
|
/**
|
|
22451
22435
|
* Queue a user prompt in SSM for shared UI rendering and lifecycle handling.
|
|
@@ -22662,325 +22646,763 @@ var SessionStateManager = class _SessionStateManager {
|
|
|
22662
22646
|
this.queuedMessages = pausedQueue;
|
|
22663
22647
|
this.store.notify("queued_messages_updated" /* QueuedMessagesUpdated */);
|
|
22664
22648
|
}
|
|
22665
|
-
clearQueue() {
|
|
22666
|
-
this.queuedMessages.clear();
|
|
22667
|
-
this.store.notify("queued_messages_updated" /* QueuedMessagesUpdated */);
|
|
22649
|
+
clearQueue() {
|
|
22650
|
+
this.queuedMessages.clear();
|
|
22651
|
+
this.store.notify("queued_messages_updated" /* QueuedMessagesUpdated */);
|
|
22652
|
+
}
|
|
22653
|
+
mergeOptimisticMessages(realMessages) {
|
|
22654
|
+
const entries = Array.from(this.optimisticMessages.values()).sort(
|
|
22655
|
+
(a, b) => (a.messages[0]?.createdAt ?? 0) - (b.messages[0]?.createdAt ?? 0)
|
|
22656
|
+
);
|
|
22657
|
+
const historicalSystemMessages = entries.filter(
|
|
22658
|
+
(entry) => entry.messages[0]?.role === "system" /* System */ && entry.leadingOrder === void 0
|
|
22659
|
+
).flatMap((entry) => entry.messages);
|
|
22660
|
+
const leadingMessages = entries.filter((entry) => entry.leadingOrder !== void 0).sort((a, b) => (a.leadingOrder ?? 0) - (b.leadingOrder ?? 0)).flatMap((entry) => entry.messages);
|
|
22661
|
+
const pendingMessages = entries.filter(
|
|
22662
|
+
(entry) => entry.messages[0]?.role !== "system" /* System */ && entry.leadingOrder === void 0
|
|
22663
|
+
).flatMap((entry) => entry.messages);
|
|
22664
|
+
const leadingInsertIndex = leadingMessages.length === 0 ? 0 : this.findLeadingInsertIndex(realMessages);
|
|
22665
|
+
return [
|
|
22666
|
+
...historicalSystemMessages,
|
|
22667
|
+
...realMessages.slice(0, leadingInsertIndex),
|
|
22668
|
+
...leadingMessages,
|
|
22669
|
+
...realMessages.slice(leadingInsertIndex),
|
|
22670
|
+
...pendingMessages
|
|
22671
|
+
];
|
|
22672
|
+
}
|
|
22673
|
+
/**
|
|
22674
|
+
* Where the leading optimistic cards render: after the last real message
|
|
22675
|
+
* that is one of their recorded siblings, or ahead of the transcript when
|
|
22676
|
+
* none has been recorded yet.
|
|
22677
|
+
*/
|
|
22678
|
+
findLeadingInsertIndex(realMessages) {
|
|
22679
|
+
if (this.confirmedLeadingMessageIds.size === 0) {
|
|
22680
|
+
return 0;
|
|
22681
|
+
}
|
|
22682
|
+
let insertIndex = 0;
|
|
22683
|
+
realMessages.forEach((message, index) => {
|
|
22684
|
+
if (this.confirmedLeadingMessageIds.has(message.id)) {
|
|
22685
|
+
insertIndex = index + 1;
|
|
22686
|
+
}
|
|
22687
|
+
});
|
|
22688
|
+
return insertIndex;
|
|
22689
|
+
}
|
|
22690
|
+
/**
|
|
22691
|
+
* Note that `messageId` is the recorded form of a leading optimistic card,
|
|
22692
|
+
* so `findLeadingInsertIndex` can keep the cards that have not been recorded
|
|
22693
|
+
* yet below it. Ignores request ids that never staged a leading card.
|
|
22694
|
+
*/
|
|
22695
|
+
rememberConfirmedLeadingMessage(requestId, messageId) {
|
|
22696
|
+
if (this.optimisticMessages.get(requestId)?.leadingOrder === void 0) {
|
|
22697
|
+
return;
|
|
22698
|
+
}
|
|
22699
|
+
this.confirmedLeadingMessageIds.add(messageId);
|
|
22700
|
+
}
|
|
22701
|
+
/**
|
|
22702
|
+
* Get messages filtered for UI display.
|
|
22703
|
+
* Uses the canonical filterMessagesForUI utility to ensure consistent filtering.
|
|
22704
|
+
* Includes optimistic messages that are pending server confirmation.
|
|
22705
|
+
*/
|
|
22706
|
+
getDisplayMessages() {
|
|
22707
|
+
const sorted = this.mergeOptimisticMessages(
|
|
22708
|
+
filterMessagesForUI2(this.store.getMessages())
|
|
22709
|
+
);
|
|
22710
|
+
if (this.renderLimit !== null && sorted.length > this.renderLimit) {
|
|
22711
|
+
return sorted.slice(-this.renderLimit);
|
|
22712
|
+
}
|
|
22713
|
+
return sorted;
|
|
22714
|
+
}
|
|
22715
|
+
/**
|
|
22716
|
+
* Add an optimistic message that will be shown until confirmed by server.
|
|
22717
|
+
*
|
|
22718
|
+
* `leadingOrder` renders the message above the transcript instead of among
|
|
22719
|
+
* the pending turns, at the position that number gives it among the other
|
|
22720
|
+
* leading messages. Session-launch steps use it so their cards sit where
|
|
22721
|
+
* the daemon records the real ones: ahead of the first user message.
|
|
22722
|
+
*/
|
|
22723
|
+
addOptimisticMessage(requestId, message, options) {
|
|
22724
|
+
this.addOptimisticMessages(requestId, [message], options);
|
|
22725
|
+
}
|
|
22726
|
+
/**
|
|
22727
|
+
* Replaces an optimistic exchange atomically so a tool result can settle
|
|
22728
|
+
* its staged tool use without briefly removing or separating the pair.
|
|
22729
|
+
*/
|
|
22730
|
+
addOptimisticMessages(requestId, messages, options) {
|
|
22731
|
+
if (messages.length === 0) {
|
|
22732
|
+
return;
|
|
22733
|
+
}
|
|
22734
|
+
this.optimisticMessages.set(requestId, {
|
|
22735
|
+
messages,
|
|
22736
|
+
...options?.leadingOrder !== void 0 ? { leadingOrder: options.leadingOrder } : {}
|
|
22737
|
+
});
|
|
22738
|
+
this.store.notifyMessagesChanged();
|
|
22739
|
+
}
|
|
22740
|
+
/**
|
|
22741
|
+
* Inspect a single optimistic message by requestId. Used by the
|
|
22742
|
+
* post-load reconciliation pass in `MultiSessionStateManager` to
|
|
22743
|
+
* match optimistic submits against the daemon-persisted user
|
|
22744
|
+
* messages and confirm them when the originating CREATE_MESSAGE
|
|
22745
|
+
* notification was lost.
|
|
22746
|
+
*/
|
|
22747
|
+
getOptimisticMessage(requestId) {
|
|
22748
|
+
return this.optimisticMessages.get(requestId)?.messages[0];
|
|
22749
|
+
}
|
|
22750
|
+
/** Keeps the unconfirmed half of a staged exchange until it arrives. */
|
|
22751
|
+
confirmOptimisticMessages(requestId, messageId) {
|
|
22752
|
+
const entry = this.optimisticMessages.get(requestId);
|
|
22753
|
+
if (!entry) {
|
|
22754
|
+
return;
|
|
22755
|
+
}
|
|
22756
|
+
const containsConfirmedMessage = entry.messages.some(
|
|
22757
|
+
(message) => message.id === messageId
|
|
22758
|
+
);
|
|
22759
|
+
const remainingMessages = entry.messages.filter(
|
|
22760
|
+
(message) => message.id !== messageId
|
|
22761
|
+
);
|
|
22762
|
+
if (containsConfirmedMessage && remainingMessages.length > 0) {
|
|
22763
|
+
this.optimisticMessages.set(requestId, {
|
|
22764
|
+
...entry,
|
|
22765
|
+
messages: remainingMessages
|
|
22766
|
+
});
|
|
22767
|
+
return;
|
|
22768
|
+
}
|
|
22769
|
+
this.optimisticMessages.delete(requestId);
|
|
22770
|
+
}
|
|
22771
|
+
/**
|
|
22772
|
+
* Remove optimistic message when confirmed or on error
|
|
22773
|
+
*/
|
|
22774
|
+
removeOptimisticMessage(requestId) {
|
|
22775
|
+
if (this.optimisticMessages.delete(requestId)) {
|
|
22776
|
+
this.store.notifyMessagesChanged();
|
|
22777
|
+
}
|
|
22778
|
+
}
|
|
22779
|
+
/**
|
|
22780
|
+
* Update session settings (model ID, reasoning effort, and autonomy level)
|
|
22781
|
+
*/
|
|
22782
|
+
updateSessionSettings(params) {
|
|
22783
|
+
if (params.modelId !== void 0) {
|
|
22784
|
+
this.store.setModelId(params.modelId);
|
|
22785
|
+
}
|
|
22786
|
+
if (params.reasoningEffort !== void 0) {
|
|
22787
|
+
this.store.setReasoningEffort(params.reasoningEffort);
|
|
22788
|
+
}
|
|
22789
|
+
this.applyInteractionSettings(params);
|
|
22790
|
+
if (params.tags !== void 0) {
|
|
22791
|
+
this.store.setTags(params.tags);
|
|
22792
|
+
}
|
|
22793
|
+
}
|
|
22794
|
+
/**
|
|
22795
|
+
* Resolve and apply interaction settings (interactionMode, autonomyLevel)
|
|
22796
|
+
* to the session store.
|
|
22797
|
+
*/
|
|
22798
|
+
applyInteractionSettings(settings) {
|
|
22799
|
+
if (settings.interactionMode === void 0 && settings.autonomyLevel === void 0) {
|
|
22800
|
+
return;
|
|
22801
|
+
}
|
|
22802
|
+
const nextInteractionSettings = resolveInteractionSettings({
|
|
22803
|
+
interactionMode: settings.interactionMode,
|
|
22804
|
+
autonomyLevel: settings.autonomyLevel,
|
|
22805
|
+
fallback: {
|
|
22806
|
+
interactionMode: this.store.getInteractionMode() ?? "auto" /* Auto */,
|
|
22807
|
+
autonomyLevel: this.store.getAutonomyLevel() ?? "off" /* Off */
|
|
22808
|
+
}
|
|
22809
|
+
});
|
|
22810
|
+
this.store.setInteractionMode(nextInteractionSettings.interactionMode);
|
|
22811
|
+
this.store.setAutonomyLevel(nextInteractionSettings.autonomyLevel);
|
|
22812
|
+
}
|
|
22813
|
+
/**
|
|
22814
|
+
* Get current interaction mode
|
|
22815
|
+
*/
|
|
22816
|
+
getInteractionMode() {
|
|
22817
|
+
return this.store.getInteractionMode();
|
|
22818
|
+
}
|
|
22819
|
+
/**
|
|
22820
|
+
* Get current autonomy level
|
|
22821
|
+
*/
|
|
22822
|
+
getAutonomyLevel() {
|
|
22823
|
+
return this.store.getAutonomyLevel();
|
|
22824
|
+
}
|
|
22825
|
+
/**
|
|
22826
|
+
* Get current model ID
|
|
22827
|
+
*/
|
|
22828
|
+
getModelId() {
|
|
22829
|
+
return this.store.getModelId();
|
|
22830
|
+
}
|
|
22831
|
+
/**
|
|
22832
|
+
* Get current reasoning effort
|
|
22833
|
+
*/
|
|
22834
|
+
getReasoningEffort() {
|
|
22835
|
+
return this.store.getReasoningEffort();
|
|
22836
|
+
}
|
|
22837
|
+
/**
|
|
22838
|
+
* Get spec mode model ID
|
|
22839
|
+
*/
|
|
22840
|
+
getSpecModeModelId() {
|
|
22841
|
+
return this.store.getSpecModeModelId();
|
|
22842
|
+
}
|
|
22843
|
+
/**
|
|
22844
|
+
* Get spec mode reasoning effort
|
|
22845
|
+
*/
|
|
22846
|
+
getSpecModeReasoningEffort() {
|
|
22847
|
+
return this.store.getSpecModeReasoningEffort();
|
|
22668
22848
|
}
|
|
22669
|
-
|
|
22670
|
-
|
|
22671
|
-
|
|
22672
|
-
|
|
22673
|
-
|
|
22674
|
-
(entry) => entry.messages[0]?.role === "system" /* System */ && entry.leadingOrder === void 0
|
|
22675
|
-
).flatMap((entry) => entry.messages);
|
|
22676
|
-
const leadingMessages = entries.filter((entry) => entry.leadingOrder !== void 0).sort((a, b) => (a.leadingOrder ?? 0) - (b.leadingOrder ?? 0)).flatMap((entry) => entry.messages);
|
|
22677
|
-
const pendingMessages = entries.filter(
|
|
22678
|
-
(entry) => entry.messages[0]?.role !== "system" /* System */ && entry.leadingOrder === void 0
|
|
22679
|
-
).flatMap((entry) => entry.messages);
|
|
22680
|
-
const leadingInsertIndex = leadingMessages.length === 0 ? 0 : this.findLeadingInsertIndex(realMessages);
|
|
22681
|
-
return [
|
|
22682
|
-
...historicalSystemMessages,
|
|
22683
|
-
...realMessages.slice(0, leadingInsertIndex),
|
|
22684
|
-
...leadingMessages,
|
|
22685
|
-
...realMessages.slice(leadingInsertIndex),
|
|
22686
|
-
...pendingMessages
|
|
22687
|
-
];
|
|
22849
|
+
/**
|
|
22850
|
+
* Get available models
|
|
22851
|
+
*/
|
|
22852
|
+
getAvailableModels() {
|
|
22853
|
+
return this.store.getAvailableModels();
|
|
22688
22854
|
}
|
|
22855
|
+
// ============ Terminal Registry Methods ============
|
|
22689
22856
|
/**
|
|
22690
|
-
*
|
|
22691
|
-
*
|
|
22692
|
-
* none has been recorded yet.
|
|
22857
|
+
* Register a mounted terminal's write handler.
|
|
22858
|
+
* Also flushes any buffered data that arrived while unmounted.
|
|
22693
22859
|
*/
|
|
22694
|
-
|
|
22695
|
-
|
|
22696
|
-
|
|
22860
|
+
registerTerminal(terminalId, writeHandler) {
|
|
22861
|
+
this.terminalWriteHandlers.set(terminalId, writeHandler);
|
|
22862
|
+
const buffered = this.store.getTerminalBufferedData(terminalId);
|
|
22863
|
+
if (buffered) {
|
|
22864
|
+
logInfo("[SessionStateManager] Flushing buffered terminal data", {
|
|
22865
|
+
terminalId
|
|
22866
|
+
});
|
|
22867
|
+
writeHandler(buffered);
|
|
22868
|
+
this.store.clearTerminalBufferedData(terminalId);
|
|
22697
22869
|
}
|
|
22698
|
-
|
|
22699
|
-
|
|
22700
|
-
|
|
22701
|
-
|
|
22702
|
-
|
|
22870
|
+
}
|
|
22871
|
+
/**
|
|
22872
|
+
* Unregister when terminal unmounts.
|
|
22873
|
+
* Future DATA notifications will be buffered.
|
|
22874
|
+
*/
|
|
22875
|
+
unregisterTerminal(terminalId) {
|
|
22876
|
+
this.terminalWriteHandlers.delete(terminalId);
|
|
22877
|
+
logInfo("[SessionStateManager] Terminal unregistered", { terminalId });
|
|
22878
|
+
}
|
|
22879
|
+
/**
|
|
22880
|
+
* Store serialized terminal state (called on unmount)
|
|
22881
|
+
*/
|
|
22882
|
+
storeTerminalState(terminalId, state) {
|
|
22883
|
+
this.store.storeTerminalSerializedState(terminalId, state);
|
|
22884
|
+
logInfo("[SessionStateManager] Terminal state serialized", {
|
|
22885
|
+
terminalId
|
|
22703
22886
|
});
|
|
22704
|
-
return insertIndex;
|
|
22705
22887
|
}
|
|
22706
22888
|
/**
|
|
22707
|
-
*
|
|
22708
|
-
* so `findLeadingInsertIndex` can keep the cards that have not been recorded
|
|
22709
|
-
* yet below it. Ignores request ids that never staged a leading card.
|
|
22889
|
+
* Get serialized state for restoration
|
|
22710
22890
|
*/
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
return;
|
|
22714
|
-
}
|
|
22715
|
-
this.confirmedLeadingMessageIds.add(messageId);
|
|
22891
|
+
getTerminalSerializedState(terminalId) {
|
|
22892
|
+
return this.store.getTerminalSerializedState(terminalId);
|
|
22716
22893
|
}
|
|
22717
22894
|
/**
|
|
22718
|
-
* Get
|
|
22719
|
-
* Uses the canonical filterMessagesForUI utility to ensure consistent filtering.
|
|
22720
|
-
* Includes optimistic messages that are pending server confirmation.
|
|
22895
|
+
* Get buffered data for restoration
|
|
22721
22896
|
*/
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
filterMessagesForUI2(this.store.getMessages())
|
|
22725
|
-
);
|
|
22726
|
-
if (this.renderLimit !== null && sorted.length > this.renderLimit) {
|
|
22727
|
-
return sorted.slice(-this.renderLimit);
|
|
22728
|
-
}
|
|
22729
|
-
return sorted;
|
|
22897
|
+
getTerminalBufferedData(terminalId) {
|
|
22898
|
+
return this.store.getTerminalBufferedData(terminalId);
|
|
22730
22899
|
}
|
|
22731
22900
|
/**
|
|
22732
|
-
*
|
|
22733
|
-
*
|
|
22734
|
-
* `leadingOrder` renders the message above the transcript instead of among
|
|
22735
|
-
* the pending turns, at the position that number gives it among the other
|
|
22736
|
-
* leading messages. Session-launch steps use it so their cards sit where
|
|
22737
|
-
* the daemon records the real ones: ahead of the first user message.
|
|
22901
|
+
* Clear only buffered data (called when daemon state is restored)
|
|
22738
22902
|
*/
|
|
22739
|
-
|
|
22740
|
-
this.
|
|
22903
|
+
clearTerminalBufferedData(terminalId) {
|
|
22904
|
+
this.store.clearTerminalBufferedData(terminalId);
|
|
22741
22905
|
}
|
|
22742
22906
|
/**
|
|
22743
|
-
*
|
|
22744
|
-
* its staged tool use without briefly removing or separating the pair.
|
|
22907
|
+
* Clear restoration state after terminal is restored
|
|
22745
22908
|
*/
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
|
|
22749
|
-
|
|
22750
|
-
this.optimisticMessages.set(requestId, {
|
|
22751
|
-
messages,
|
|
22752
|
-
...options?.leadingOrder !== void 0 ? { leadingOrder: options.leadingOrder } : {}
|
|
22909
|
+
clearTerminalRestorationState(terminalId) {
|
|
22910
|
+
this.store.clearTerminalRestorationState(terminalId);
|
|
22911
|
+
logInfo("[SessionStateManager] Terminal restoration state cleared", {
|
|
22912
|
+
terminalId
|
|
22753
22913
|
});
|
|
22754
|
-
this.store.notifyMessagesChanged();
|
|
22755
22914
|
}
|
|
22756
22915
|
/**
|
|
22757
|
-
*
|
|
22758
|
-
* post-load reconciliation pass in `MultiSessionStateManager` to
|
|
22759
|
-
* match optimistic submits against the daemon-persisted user
|
|
22760
|
-
* messages and confirm them when the originating CREATE_MESSAGE
|
|
22761
|
-
* notification was lost.
|
|
22916
|
+
* Get the machine ID for this session
|
|
22762
22917
|
*/
|
|
22763
|
-
|
|
22764
|
-
return this.
|
|
22918
|
+
getMachineId() {
|
|
22919
|
+
return this.store.getMachineId();
|
|
22765
22920
|
}
|
|
22766
|
-
/**
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22921
|
+
/**
|
|
22922
|
+
* Set the machine ID for this session
|
|
22923
|
+
*/
|
|
22924
|
+
setMachineId(machineId) {
|
|
22925
|
+
this.store.setMachineId(machineId);
|
|
22926
|
+
}
|
|
22927
|
+
// ============ UI Render Cutoff ============
|
|
22928
|
+
setUiRenderCutoff(messageId) {
|
|
22929
|
+
this.store.setUiRenderCutoff(messageId);
|
|
22930
|
+
if (messageId) {
|
|
22931
|
+
logInfo("[SessionStateManager] UI render cutoff set", {
|
|
22932
|
+
cutoffMessageId: messageId
|
|
22933
|
+
});
|
|
22934
|
+
}
|
|
22935
|
+
}
|
|
22936
|
+
getUiRenderCutoff() {
|
|
22937
|
+
return this.store.getUiRenderCutoff();
|
|
22938
|
+
}
|
|
22939
|
+
getUiRenderHiddenMessageCount() {
|
|
22940
|
+
return this.store.getUiRenderHiddenMessageCount();
|
|
22941
|
+
}
|
|
22942
|
+
/**
|
|
22943
|
+
* Double the render limit (or expand fully if doubling exceeds total count).
|
|
22944
|
+
* Returns true if there are still more messages to expand, false if fully
|
|
22945
|
+
* expanded. No-op unless progressive UI rendering is enabled.
|
|
22946
|
+
*/
|
|
22947
|
+
expandUiMessages() {
|
|
22948
|
+
if (this.renderLimit === null) {
|
|
22949
|
+
return false;
|
|
22950
|
+
}
|
|
22951
|
+
const displayCount = filterMessagesForUI2(this.store.getMessages()).length;
|
|
22952
|
+
const nextLimit = this.renderLimit * 2;
|
|
22953
|
+
if (nextLimit >= displayCount) {
|
|
22954
|
+
this.renderLimit = null;
|
|
22955
|
+
} else {
|
|
22956
|
+
this.renderLimit = nextLimit;
|
|
22957
|
+
}
|
|
22958
|
+
this.store.notifyMessagesChanged();
|
|
22959
|
+
return this.renderLimit !== null;
|
|
22960
|
+
}
|
|
22961
|
+
addMessage(message, options) {
|
|
22962
|
+
this.applyTodoStateFromMessage(message);
|
|
22963
|
+
this.store.addMessage(message, options);
|
|
22964
|
+
}
|
|
22965
|
+
// ============ Conversation State Queries ============
|
|
22966
|
+
isConversationEmpty() {
|
|
22967
|
+
return this.store.getMessageCount() === 0 && this.optimisticMessages.size === 0;
|
|
22968
|
+
}
|
|
22969
|
+
};
|
|
22970
|
+
|
|
22971
|
+
// ../daemon-sdk/src/session/handlers/AskUserRequestHandler.ts
|
|
22972
|
+
import { EventEmitter as EventEmitter6 } from "eventemitter3";
|
|
22973
|
+
var AskUserRequestHandler = class _AskUserRequestHandler extends EventEmitter6 {
|
|
22974
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
22975
|
+
static buildResolutionContext(pending) {
|
|
22976
|
+
return {
|
|
22977
|
+
sessionId: pending.sessionId,
|
|
22978
|
+
associatedSessionIds: [pending.sessionId],
|
|
22979
|
+
...pending.machineId ? { machineId: pending.machineId } : {}
|
|
22980
|
+
};
|
|
22981
|
+
}
|
|
22982
|
+
handleAskUserRequest(request, id, sessionId, context) {
|
|
22983
|
+
logInfo("[AskUserRequestHandler] Handling ask-user request", {
|
|
22984
|
+
requestId: id,
|
|
22985
|
+
sessionId,
|
|
22986
|
+
toolCallId: request.params.toolCallId,
|
|
22987
|
+
count: request.params.questions.length
|
|
22988
|
+
});
|
|
22989
|
+
const machineId = context?.machineId ?? this.pendingRequests.get(id)?.machineId;
|
|
22990
|
+
const pending = {
|
|
22991
|
+
requestId: id,
|
|
22992
|
+
sessionId,
|
|
22993
|
+
toolCallId: request.params.toolCallId,
|
|
22994
|
+
questions: request.params.questions,
|
|
22995
|
+
timestamp: Date.now(),
|
|
22996
|
+
...context?.isRestored ? { isRestored: true } : {},
|
|
22997
|
+
...machineId ? { machineId } : {}
|
|
22998
|
+
};
|
|
22999
|
+
const existingRequest = this.pendingRequests.get(id);
|
|
23000
|
+
if (existingRequest) {
|
|
23001
|
+
this.pendingRequests.set(id, pending);
|
|
23002
|
+
logInfo("[AskUserRequestHandler] Duplicate ask-user request replay", {
|
|
23003
|
+
requestId: id,
|
|
23004
|
+
sessionId
|
|
23005
|
+
});
|
|
22770
23006
|
return;
|
|
22771
23007
|
}
|
|
22772
|
-
|
|
22773
|
-
|
|
23008
|
+
this.pendingRequests.set(id, pending);
|
|
23009
|
+
this.emit("askUserRequested", pending);
|
|
23010
|
+
}
|
|
23011
|
+
resolveAskUser(id, result) {
|
|
23012
|
+
const pending = this.pendingRequests.get(id);
|
|
23013
|
+
if (!pending) {
|
|
23014
|
+
logWarn("[AskUserRequestHandler] No pending ask-user request found", {
|
|
23015
|
+
requestId: id
|
|
23016
|
+
});
|
|
23017
|
+
return;
|
|
23018
|
+
}
|
|
23019
|
+
_AskUserRequestHandler.validateResult(pending, result);
|
|
23020
|
+
this.pendingRequests.delete(id);
|
|
23021
|
+
this.emit(
|
|
23022
|
+
"askUserResolved",
|
|
23023
|
+
id,
|
|
23024
|
+
result,
|
|
23025
|
+
_AskUserRequestHandler.buildResolutionContext(pending)
|
|
22774
23026
|
);
|
|
22775
|
-
|
|
22776
|
-
|
|
23027
|
+
}
|
|
23028
|
+
clearAskUser(id) {
|
|
23029
|
+
const pending = this.pendingRequests.get(id);
|
|
23030
|
+
if (!pending) {
|
|
23031
|
+
logWarn(
|
|
23032
|
+
"[AskUserRequestHandler] No pending ask-user request found to clear",
|
|
23033
|
+
{
|
|
23034
|
+
requestId: id
|
|
23035
|
+
}
|
|
23036
|
+
);
|
|
23037
|
+
return;
|
|
23038
|
+
}
|
|
23039
|
+
this.pendingRequests.delete(id);
|
|
23040
|
+
this.emit(
|
|
23041
|
+
"askUserResolved",
|
|
23042
|
+
id,
|
|
23043
|
+
void 0,
|
|
23044
|
+
_AskUserRequestHandler.buildResolutionContext(pending)
|
|
23045
|
+
);
|
|
23046
|
+
}
|
|
23047
|
+
clearSessionAskUserRequests(sessionId) {
|
|
23048
|
+
const requestsToRemove = Array.from(this.pendingRequests.values()).filter(
|
|
23049
|
+
(request) => request.sessionId === sessionId
|
|
22777
23050
|
);
|
|
22778
|
-
|
|
22779
|
-
this.
|
|
22780
|
-
...entry,
|
|
22781
|
-
messages: remainingMessages
|
|
22782
|
-
});
|
|
22783
|
-
return;
|
|
23051
|
+
for (const request of requestsToRemove) {
|
|
23052
|
+
this.clearAskUser(request.requestId);
|
|
22784
23053
|
}
|
|
22785
|
-
this.optimisticMessages.delete(requestId);
|
|
22786
23054
|
}
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
*/
|
|
22790
|
-
removeOptimisticMessage(requestId) {
|
|
22791
|
-
if (this.optimisticMessages.delete(requestId)) {
|
|
22792
|
-
this.store.notifyMessagesChanged();
|
|
22793
|
-
}
|
|
23055
|
+
cancelAskUser(id) {
|
|
23056
|
+
this.resolveAskUser(id, { cancelled: true, answers: [] });
|
|
22794
23057
|
}
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
23058
|
+
getPendingAskUserRequests() {
|
|
23059
|
+
return Array.from(this.pendingRequests.values());
|
|
23060
|
+
}
|
|
23061
|
+
getPendingAskUserRequest(id) {
|
|
23062
|
+
return this.pendingRequests.get(id);
|
|
23063
|
+
}
|
|
23064
|
+
clearAll() {
|
|
23065
|
+
this.pendingRequests.clear();
|
|
23066
|
+
}
|
|
23067
|
+
destroy() {
|
|
23068
|
+
this.clearAll();
|
|
23069
|
+
this.removeAllListeners();
|
|
23070
|
+
}
|
|
23071
|
+
static validateResult(pending, result) {
|
|
23072
|
+
if (result.cancelled === true) {
|
|
23073
|
+
return;
|
|
22801
23074
|
}
|
|
22802
|
-
|
|
22803
|
-
|
|
23075
|
+
const questionsByIndex = new Map(
|
|
23076
|
+
pending.questions.map((q) => [q.index, q])
|
|
23077
|
+
);
|
|
23078
|
+
const answered = /* @__PURE__ */ new Set();
|
|
23079
|
+
for (const answer of result.answers) {
|
|
23080
|
+
const question = questionsByIndex.get(answer.index);
|
|
23081
|
+
if (!question) {
|
|
23082
|
+
throw new MetaError("Answer references unknown question index", {
|
|
23083
|
+
requestId: pending.requestId,
|
|
23084
|
+
index: answer.index
|
|
23085
|
+
});
|
|
23086
|
+
}
|
|
23087
|
+
if (answered.has(answer.index)) {
|
|
23088
|
+
throw new MetaError("Duplicate answers for question index", {
|
|
23089
|
+
requestId: pending.requestId,
|
|
23090
|
+
index: answer.index
|
|
23091
|
+
});
|
|
23092
|
+
}
|
|
23093
|
+
answered.add(answer.index);
|
|
22804
23094
|
}
|
|
22805
|
-
|
|
22806
|
-
if (
|
|
22807
|
-
|
|
23095
|
+
const missing = pending.questions.map((q) => q.index).filter((idx) => !answered.has(idx));
|
|
23096
|
+
if (missing.length > 0) {
|
|
23097
|
+
throw new MetaError("Missing answers for some questions", {
|
|
23098
|
+
requestId: pending.requestId,
|
|
23099
|
+
missing: missing.map(String)
|
|
23100
|
+
});
|
|
22808
23101
|
}
|
|
22809
23102
|
}
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
22815
|
-
|
|
22816
|
-
|
|
23103
|
+
};
|
|
23104
|
+
|
|
23105
|
+
// ../daemon-sdk/src/session/handlers/PendingUserActionStore.ts
|
|
23106
|
+
var PendingUserActionStore = class {
|
|
23107
|
+
permissionsByToolUseId = /* @__PURE__ */ new Map();
|
|
23108
|
+
askUsersBySession = /* @__PURE__ */ new Map();
|
|
23109
|
+
savePermission(action) {
|
|
23110
|
+
this.permissionsByToolUseId.set(action.toolUseId, action);
|
|
23111
|
+
logInfo("[PendingUserActionStore] Stored permission for later replay", {
|
|
23112
|
+
sessionId: action.sessionId,
|
|
23113
|
+
requestId: action.requestId,
|
|
23114
|
+
toolUseId: action.toolUseId,
|
|
23115
|
+
selectedOptionLabel: action.selectedOption
|
|
23116
|
+
});
|
|
23117
|
+
}
|
|
23118
|
+
saveAskUser(action) {
|
|
23119
|
+
let map = this.askUsersBySession.get(action.sessionId);
|
|
23120
|
+
if (!map) {
|
|
23121
|
+
map = /* @__PURE__ */ new Map();
|
|
23122
|
+
this.askUsersBySession.set(action.sessionId, map);
|
|
22817
23123
|
}
|
|
22818
|
-
|
|
22819
|
-
|
|
22820
|
-
|
|
22821
|
-
|
|
22822
|
-
|
|
22823
|
-
autonomyLevel: this.store.getAutonomyLevel() ?? "off" /* Off */
|
|
22824
|
-
}
|
|
23124
|
+
map.set(action.toolCallId, action);
|
|
23125
|
+
logInfo("[PendingUserActionStore] Stored ask-user for later replay", {
|
|
23126
|
+
sessionId: action.sessionId,
|
|
23127
|
+
requestId: action.requestId,
|
|
23128
|
+
toolCallId: action.toolCallId
|
|
22825
23129
|
});
|
|
22826
|
-
this.store.setInteractionMode(nextInteractionSettings.interactionMode);
|
|
22827
|
-
this.store.setAutonomyLevel(nextInteractionSettings.autonomyLevel);
|
|
22828
23130
|
}
|
|
22829
|
-
/**
|
|
22830
|
-
|
|
22831
|
-
|
|
22832
|
-
getInteractionMode() {
|
|
22833
|
-
return this.store.getInteractionMode();
|
|
23131
|
+
/** Peek a buffered permission response by its globally-unique tool-use id. */
|
|
23132
|
+
getForToolUse(toolUseId) {
|
|
23133
|
+
return this.permissionsByToolUseId.get(toolUseId);
|
|
22834
23134
|
}
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
*/
|
|
22838
|
-
getAutonomyLevel() {
|
|
22839
|
-
return this.store.getAutonomyLevel();
|
|
23135
|
+
getForToolCall(sessionId, toolCallId) {
|
|
23136
|
+
return this.askUsersBySession.get(sessionId)?.get(toolCallId);
|
|
22840
23137
|
}
|
|
22841
|
-
/**
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22845
|
-
|
|
23138
|
+
/** Remove and return a buffered permission response by tool-use id. */
|
|
23139
|
+
takeForToolUse(toolUseId) {
|
|
23140
|
+
const action = this.permissionsByToolUseId.get(toolUseId);
|
|
23141
|
+
if (!action) return void 0;
|
|
23142
|
+
this.permissionsByToolUseId.delete(toolUseId);
|
|
23143
|
+
return action;
|
|
22846
23144
|
}
|
|
22847
|
-
|
|
22848
|
-
|
|
22849
|
-
|
|
22850
|
-
|
|
22851
|
-
|
|
23145
|
+
takeForToolCall(sessionId, toolCallId) {
|
|
23146
|
+
const map = this.askUsersBySession.get(sessionId);
|
|
23147
|
+
if (!map) return void 0;
|
|
23148
|
+
const action = map.get(toolCallId);
|
|
23149
|
+
if (!action) return void 0;
|
|
23150
|
+
map.delete(toolCallId);
|
|
23151
|
+
if (map.size === 0) this.askUsersBySession.delete(sessionId);
|
|
23152
|
+
return action;
|
|
22852
23153
|
}
|
|
22853
|
-
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
|
|
22857
|
-
return this.store.getSpecModeModelId();
|
|
23154
|
+
getPermissionsForSession(sessionId) {
|
|
23155
|
+
return Array.from(this.permissionsByToolUseId.values()).filter(
|
|
23156
|
+
(action) => action.sessionId === sessionId
|
|
23157
|
+
);
|
|
22858
23158
|
}
|
|
22859
|
-
|
|
22860
|
-
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
|
|
23159
|
+
getAskUsersForSession(sessionId) {
|
|
23160
|
+
const map = this.askUsersBySession.get(sessionId);
|
|
23161
|
+
return map ? Array.from(map.values()) : [];
|
|
23162
|
+
}
|
|
23163
|
+
clearSession(sessionId) {
|
|
23164
|
+
let permCount = 0;
|
|
23165
|
+
for (const [toolUseId, action] of this.permissionsByToolUseId) {
|
|
23166
|
+
if (action.sessionId === sessionId) {
|
|
23167
|
+
this.permissionsByToolUseId.delete(toolUseId);
|
|
23168
|
+
permCount += 1;
|
|
23169
|
+
}
|
|
23170
|
+
}
|
|
23171
|
+
const askCount = this.askUsersBySession.get(sessionId)?.size ?? 0;
|
|
23172
|
+
this.askUsersBySession.delete(sessionId);
|
|
23173
|
+
const total = permCount + askCount;
|
|
23174
|
+
if (total > 0) {
|
|
23175
|
+
logInfo("[PendingUserActionStore] Cleared session", {
|
|
23176
|
+
sessionId,
|
|
23177
|
+
pendingCount: permCount,
|
|
23178
|
+
totalQuestions: askCount
|
|
23179
|
+
});
|
|
23180
|
+
}
|
|
23181
|
+
return total;
|
|
23182
|
+
}
|
|
23183
|
+
clearAll() {
|
|
23184
|
+
this.permissionsByToolUseId.clear();
|
|
23185
|
+
this.askUsersBySession.clear();
|
|
23186
|
+
}
|
|
23187
|
+
destroy() {
|
|
23188
|
+
this.clearAll();
|
|
22864
23189
|
}
|
|
23190
|
+
};
|
|
23191
|
+
|
|
23192
|
+
// ../daemon-sdk/src/session/handlers/PermissionRequestHandler.ts
|
|
23193
|
+
import { EventEmitter as EventEmitter7 } from "eventemitter3";
|
|
23194
|
+
function normalizeAssociatedSessionIds(sessionId, associatedSessionIds) {
|
|
23195
|
+
return Array.from(/* @__PURE__ */ new Set([sessionId, ...associatedSessionIds ?? []]));
|
|
23196
|
+
}
|
|
23197
|
+
var PermissionRequestHandler = class _PermissionRequestHandler extends EventEmitter7 {
|
|
23198
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
23199
|
+
statistics = {
|
|
23200
|
+
total: 0,
|
|
23201
|
+
approved: 0,
|
|
23202
|
+
denied: 0,
|
|
23203
|
+
timedOut: 0
|
|
23204
|
+
};
|
|
22865
23205
|
/**
|
|
22866
|
-
*
|
|
23206
|
+
* Handle an incoming permission request
|
|
22867
23207
|
*/
|
|
22868
|
-
|
|
22869
|
-
return
|
|
23208
|
+
static buildResolutionContext(pending) {
|
|
23209
|
+
return {
|
|
23210
|
+
sessionId: pending.sessionId,
|
|
23211
|
+
associatedSessionIds: pending.associatedSessionIds,
|
|
23212
|
+
...pending.machineId ? { machineId: pending.machineId } : {}
|
|
23213
|
+
};
|
|
23214
|
+
}
|
|
23215
|
+
handlePermissionRequest(request, id, sessionId, context) {
|
|
23216
|
+
logInfo("[PermissionRequestHandler] Handling permission request", {
|
|
23217
|
+
requestId: id,
|
|
23218
|
+
sessionId,
|
|
23219
|
+
count: request.params.toolUses.length,
|
|
23220
|
+
options: request.params.options
|
|
23221
|
+
});
|
|
23222
|
+
const { options } = request.params;
|
|
23223
|
+
const toolUses = request.params.toolUses.map((tool) => ({
|
|
23224
|
+
...tool,
|
|
23225
|
+
toolUse: {
|
|
23226
|
+
...tool.toolUse,
|
|
23227
|
+
input: getPermissionToolInputForDisplay2({
|
|
23228
|
+
confirmationType: tool.confirmationType,
|
|
23229
|
+
details: tool.details,
|
|
23230
|
+
toolInput: tool.toolUse.input
|
|
23231
|
+
})
|
|
23232
|
+
}
|
|
23233
|
+
}));
|
|
23234
|
+
const requestAssociatedSessionIds = normalizeAssociatedSessionIds(
|
|
23235
|
+
sessionId,
|
|
23236
|
+
request.params.associatedSessionIds
|
|
23237
|
+
);
|
|
23238
|
+
const existingPermission = this.pendingPermissions.get(id);
|
|
23239
|
+
const associatedSessionIds = existingPermission ? normalizeAssociatedSessionIds(sessionId, [
|
|
23240
|
+
...existingPermission.associatedSessionIds,
|
|
23241
|
+
...requestAssociatedSessionIds
|
|
23242
|
+
]) : requestAssociatedSessionIds;
|
|
23243
|
+
const machineId = context?.machineId ?? existingPermission?.machineId;
|
|
23244
|
+
const pendingPermission = {
|
|
23245
|
+
requestId: id,
|
|
23246
|
+
sessionId,
|
|
23247
|
+
associatedSessionIds,
|
|
23248
|
+
toolUses,
|
|
23249
|
+
options,
|
|
23250
|
+
timestamp: Date.now(),
|
|
23251
|
+
...context?.isRestored ? { isRestored: true } : {},
|
|
23252
|
+
...machineId ? { machineId } : {}
|
|
23253
|
+
};
|
|
23254
|
+
if (existingPermission) {
|
|
23255
|
+
this.pendingPermissions.set(id, pendingPermission);
|
|
23256
|
+
const addedSurface = associatedSessionIds.some(
|
|
23257
|
+
(associatedSessionId) => !existingPermission.associatedSessionIds.includes(associatedSessionId)
|
|
23258
|
+
);
|
|
23259
|
+
logInfo(
|
|
23260
|
+
"[PermissionRequestHandler] Duplicate permission request replay",
|
|
23261
|
+
{
|
|
23262
|
+
requestId: id,
|
|
23263
|
+
sessionId
|
|
23264
|
+
}
|
|
23265
|
+
);
|
|
23266
|
+
if (addedSurface) {
|
|
23267
|
+
this.emit("permissionRequested", pendingPermission);
|
|
23268
|
+
}
|
|
23269
|
+
return;
|
|
23270
|
+
}
|
|
23271
|
+
const incomingToolUseIds = new Set(
|
|
23272
|
+
toolUses.map((toolUse) => toolUse.toolUse.id)
|
|
23273
|
+
);
|
|
23274
|
+
for (const [pendingId, pending] of this.pendingPermissions.entries()) {
|
|
23275
|
+
if (pending.machineId && machineId && pending.machineId !== machineId) {
|
|
23276
|
+
continue;
|
|
23277
|
+
}
|
|
23278
|
+
if (pending.toolUses.some(
|
|
23279
|
+
(toolUse) => incomingToolUseIds.has(toolUse.toolUse.id)
|
|
23280
|
+
)) {
|
|
23281
|
+
this.clearPermission(pendingId, { emitResolved: true });
|
|
23282
|
+
}
|
|
23283
|
+
}
|
|
23284
|
+
this.pendingPermissions.set(id, pendingPermission);
|
|
23285
|
+
this.statistics.total++;
|
|
23286
|
+
logInfo("[PermissionRequestHandler] Stored pending permission", {
|
|
23287
|
+
requestId: id,
|
|
23288
|
+
count: this.pendingPermissions.size
|
|
23289
|
+
});
|
|
23290
|
+
logInfo("[PermissionRequestHandler] Emitting permissionRequested event", {
|
|
23291
|
+
requestId: id
|
|
23292
|
+
});
|
|
23293
|
+
this.emit("permissionRequested", pendingPermission);
|
|
22870
23294
|
}
|
|
22871
|
-
// ============ Terminal Registry Methods ============
|
|
22872
23295
|
/**
|
|
22873
|
-
*
|
|
22874
|
-
* Also flushes any buffered data that arrived while unmounted.
|
|
23296
|
+
* Resolve a permission request with user's choice
|
|
22875
23297
|
*/
|
|
22876
|
-
|
|
22877
|
-
this.
|
|
22878
|
-
|
|
22879
|
-
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
|
|
22883
|
-
|
|
22884
|
-
|
|
23298
|
+
resolvePermission(id, option) {
|
|
23299
|
+
const pending = this.pendingPermissions.get(id);
|
|
23300
|
+
if (!pending) {
|
|
23301
|
+
throw new MetaError("No pending permission found", { requestId: id });
|
|
23302
|
+
}
|
|
23303
|
+
const isValidOption = pending.options.some((opt) => opt.value === option);
|
|
23304
|
+
if (!isValidOption) {
|
|
23305
|
+
const allowedOptions = pending.options.map((opt) => opt.value);
|
|
23306
|
+
throw new MetaError(
|
|
23307
|
+
`Invalid permission option: ${option}. Allowed: ${allowedOptions.join(", ")}`,
|
|
23308
|
+
{
|
|
23309
|
+
requestId: id
|
|
23310
|
+
}
|
|
23311
|
+
);
|
|
23312
|
+
}
|
|
23313
|
+
this.pendingPermissions.delete(id);
|
|
23314
|
+
if (option !== "cancel" /* Cancel */) {
|
|
23315
|
+
this.statistics.approved++;
|
|
23316
|
+
} else {
|
|
23317
|
+
this.statistics.denied++;
|
|
22885
23318
|
}
|
|
23319
|
+
this.emit(
|
|
23320
|
+
"permissionResolved",
|
|
23321
|
+
id,
|
|
23322
|
+
option,
|
|
23323
|
+
_PermissionRequestHandler.buildResolutionContext(pending)
|
|
23324
|
+
);
|
|
22886
23325
|
}
|
|
22887
23326
|
/**
|
|
22888
|
-
*
|
|
22889
|
-
* Future DATA notifications will be buffered.
|
|
23327
|
+
* Clear a specific permission without an outcome.
|
|
22890
23328
|
*/
|
|
22891
|
-
|
|
22892
|
-
this.
|
|
22893
|
-
|
|
23329
|
+
clearPermission(id, options) {
|
|
23330
|
+
const pending = this.pendingPermissions.get(id);
|
|
23331
|
+
this.pendingPermissions.delete(id);
|
|
23332
|
+
if (options?.emitResolved) {
|
|
23333
|
+
this.emit(
|
|
23334
|
+
"permissionResolved",
|
|
23335
|
+
id,
|
|
23336
|
+
void 0,
|
|
23337
|
+
pending ? _PermissionRequestHandler.buildResolutionContext(pending) : void 0
|
|
23338
|
+
);
|
|
23339
|
+
}
|
|
22894
23340
|
}
|
|
22895
23341
|
/**
|
|
22896
|
-
*
|
|
23342
|
+
* Clear all pending permissions for a specific session.
|
|
23343
|
+
*
|
|
23344
|
+
* With `preserveRelayed`, permissions surfaced on multiple sessions (a
|
|
23345
|
+
* relayed subagent permission whose `associatedSessionIds` extend beyond this
|
|
23346
|
+
* session) are kept. loadSession uses this so reloading the parent doesn't
|
|
23347
|
+
* wipe a child's still-pending prompt that the daemon's parent-session load
|
|
23348
|
+
* never returns.
|
|
22897
23349
|
*/
|
|
22898
|
-
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
23350
|
+
clearSessionPermissions(sessionId, options) {
|
|
23351
|
+
const permissionsToRemove = [];
|
|
23352
|
+
for (const [id, permission] of this.pendingPermissions.entries()) {
|
|
23353
|
+
if (permission.sessionId !== sessionId) {
|
|
23354
|
+
continue;
|
|
23355
|
+
}
|
|
23356
|
+
if (options?.preserveRelayed && permission.associatedSessionIds.some(
|
|
23357
|
+
(associatedId) => associatedId !== sessionId
|
|
23358
|
+
)) {
|
|
23359
|
+
continue;
|
|
23360
|
+
}
|
|
23361
|
+
permissionsToRemove.push(id);
|
|
23362
|
+
}
|
|
23363
|
+
for (const id of permissionsToRemove) {
|
|
23364
|
+
this.clearPermission(id, options);
|
|
23365
|
+
}
|
|
23366
|
+
logInfo("[PermissionRequestHandler] Cleared permissions for session", {
|
|
23367
|
+
sessionId,
|
|
23368
|
+
count: permissionsToRemove.length
|
|
22902
23369
|
});
|
|
22903
23370
|
}
|
|
22904
23371
|
/**
|
|
22905
|
-
*
|
|
22906
|
-
*/
|
|
22907
|
-
getTerminalSerializedState(terminalId) {
|
|
22908
|
-
return this.store.getTerminalSerializedState(terminalId);
|
|
22909
|
-
}
|
|
22910
|
-
/**
|
|
22911
|
-
* Get buffered data for restoration
|
|
23372
|
+
* Clear all pending permissions
|
|
22912
23373
|
*/
|
|
22913
|
-
|
|
22914
|
-
|
|
23374
|
+
clearAllPermissions() {
|
|
23375
|
+
this.pendingPermissions.clear();
|
|
22915
23376
|
}
|
|
22916
23377
|
/**
|
|
22917
|
-
*
|
|
23378
|
+
* Get all pending permissions
|
|
22918
23379
|
*/
|
|
22919
|
-
|
|
22920
|
-
this.
|
|
23380
|
+
getPendingPermissions() {
|
|
23381
|
+
return Array.from(this.pendingPermissions.values());
|
|
22921
23382
|
}
|
|
22922
|
-
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
this.store.clearTerminalRestorationState(terminalId);
|
|
22927
|
-
logInfo("[SessionStateManager] Terminal restoration state cleared", {
|
|
22928
|
-
terminalId
|
|
22929
|
-
});
|
|
23383
|
+
getPendingPermissionsForSession(sessionId) {
|
|
23384
|
+
return this.getPendingPermissions().filter(
|
|
23385
|
+
(permission) => permission.associatedSessionIds.includes(sessionId)
|
|
23386
|
+
);
|
|
22930
23387
|
}
|
|
22931
23388
|
/**
|
|
22932
|
-
* Get
|
|
23389
|
+
* Get a specific pending permission
|
|
22933
23390
|
*/
|
|
22934
|
-
|
|
22935
|
-
return this.
|
|
23391
|
+
getPendingPermission(id) {
|
|
23392
|
+
return this.pendingPermissions.get(id);
|
|
22936
23393
|
}
|
|
22937
23394
|
/**
|
|
22938
|
-
*
|
|
23395
|
+
* Check if there are any pending permissions
|
|
22939
23396
|
*/
|
|
22940
|
-
|
|
22941
|
-
this.
|
|
22942
|
-
}
|
|
22943
|
-
// ============ UI Render Cutoff ============
|
|
22944
|
-
setUiRenderCutoff(messageId) {
|
|
22945
|
-
this.store.setUiRenderCutoff(messageId);
|
|
22946
|
-
if (messageId) {
|
|
22947
|
-
logInfo("[SessionStateManager] UI render cutoff set", {
|
|
22948
|
-
cutoffMessageId: messageId
|
|
22949
|
-
});
|
|
22950
|
-
}
|
|
22951
|
-
}
|
|
22952
|
-
getUiRenderCutoff() {
|
|
22953
|
-
return this.store.getUiRenderCutoff();
|
|
22954
|
-
}
|
|
22955
|
-
getUiRenderHiddenMessageCount() {
|
|
22956
|
-
return this.store.getUiRenderHiddenMessageCount();
|
|
23397
|
+
hasPendingPermissions() {
|
|
23398
|
+
return this.pendingPermissions.size > 0;
|
|
22957
23399
|
}
|
|
22958
23400
|
/**
|
|
22959
|
-
*
|
|
22960
|
-
* Returns true if there are still more messages to expand, false if fully
|
|
22961
|
-
* expanded. No-op unless progressive UI rendering is enabled.
|
|
23401
|
+
* Destroy and clean up
|
|
22962
23402
|
*/
|
|
22963
|
-
|
|
22964
|
-
|
|
22965
|
-
|
|
22966
|
-
}
|
|
22967
|
-
const displayCount = filterMessagesForUI2(this.store.getMessages()).length;
|
|
22968
|
-
const nextLimit = this.renderLimit * 2;
|
|
22969
|
-
if (nextLimit >= displayCount) {
|
|
22970
|
-
this.renderLimit = null;
|
|
22971
|
-
} else {
|
|
22972
|
-
this.renderLimit = nextLimit;
|
|
22973
|
-
}
|
|
22974
|
-
this.store.notifyMessagesChanged();
|
|
22975
|
-
return this.renderLimit !== null;
|
|
22976
|
-
}
|
|
22977
|
-
addMessage(message, options) {
|
|
22978
|
-
this.applyTodoStateFromMessage(message);
|
|
22979
|
-
this.store.addMessage(message, options);
|
|
22980
|
-
}
|
|
22981
|
-
// ============ Conversation State Queries ============
|
|
22982
|
-
isConversationEmpty() {
|
|
22983
|
-
return this.store.getMessageCount() === 0 && this.optimisticMessages.size === 0;
|
|
23403
|
+
destroy() {
|
|
23404
|
+
this.clearAllPermissions();
|
|
23405
|
+
this.removeAllListeners();
|
|
22984
23406
|
}
|
|
22985
23407
|
};
|
|
22986
23408
|
|
|
@@ -23006,6 +23428,43 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23006
23428
|
subagentInvocationSummaries = /* @__PURE__ */ new Map();
|
|
23007
23429
|
// Track sessions that have already had their initial prompt sent (persists across remounts)
|
|
23008
23430
|
sentInitialPromptSessions = /* @__PURE__ */ new Set();
|
|
23431
|
+
// Session-scoped prompt state, owned here (app lifetime) rather than by the
|
|
23432
|
+
// per-connection DaemonSessionController: pending prompts and buffered
|
|
23433
|
+
// answers must survive controller teardown/recreation (machine removal,
|
|
23434
|
+
// act-as recreation, provider remount), which otherwise wiped them while
|
|
23435
|
+
// loadState stayed Loaded and suppressed the daemon-side restore. Entries
|
|
23436
|
+
// are keyed by globally unique request ids and carry their sessionId, so a
|
|
23437
|
+
// single shared instance serves every machine's controller.
|
|
23438
|
+
_askUserRequests = new AskUserRequestHandler();
|
|
23439
|
+
_permissionRequests = new PermissionRequestHandler();
|
|
23440
|
+
_pendingUserActions = new PendingUserActionStore();
|
|
23441
|
+
// Narrowed public views: per-entry read/subscribe/mutate only. Whole-store
|
|
23442
|
+
// teardown stays internal (see removeSession / clear).
|
|
23443
|
+
get askUserRequests() {
|
|
23444
|
+
return this._askUserRequests;
|
|
23445
|
+
}
|
|
23446
|
+
get permissionRequests() {
|
|
23447
|
+
return this._permissionRequests;
|
|
23448
|
+
}
|
|
23449
|
+
get pendingUserActions() {
|
|
23450
|
+
return this._pendingUserActions;
|
|
23451
|
+
}
|
|
23452
|
+
// Session-scoped state that describes the *session* rather than the
|
|
23453
|
+
// connection, moved off the per-connection DaemonSessionController so a
|
|
23454
|
+
// replacement controller (preconnect round-trip, reconnect, machine
|
|
23455
|
+
// removal) does not lose it:
|
|
23456
|
+
// - inactiveSessions: whether the daemon paused the session's worker; pairs
|
|
23457
|
+
// with the pendingUserActions buffering above so respondToPermission /
|
|
23458
|
+
// respondToAskUser keep taking the buffer-and-self-resume path.
|
|
23459
|
+
// - sessionsWithConcurrentPrompts: bookkeeping over the prompt stores above;
|
|
23460
|
+
// must live and die with them so a rebuilt controller does not blanket
|
|
23461
|
+
// clear the surviving tail prompt of a concurrent batch.
|
|
23462
|
+
// - sessionLoadOptions: spawn options replayed on any later
|
|
23463
|
+
// ensureSessionLoaded / self-resume so the session is not respawned with
|
|
23464
|
+
// the wrong flags.
|
|
23465
|
+
inactiveSessions = /* @__PURE__ */ new Set();
|
|
23466
|
+
sessionsWithConcurrentPrompts = /* @__PURE__ */ new Set();
|
|
23467
|
+
sessionLoadOptions = /* @__PURE__ */ new Map();
|
|
23009
23468
|
onToolProgressUpdate;
|
|
23010
23469
|
isProgressiveUiRenderingEnabled;
|
|
23011
23470
|
// Store for explicit setting changes made before a concrete session store
|
|
@@ -23053,19 +23512,21 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23053
23512
|
emitSessionStateEvent = (event, payload) => {
|
|
23054
23513
|
for (const listener of this.sessionStateEvents.listeners(event)) {
|
|
23055
23514
|
try {
|
|
23056
|
-
listener
|
|
23515
|
+
Reflect.apply(listener, void 0, [payload]);
|
|
23057
23516
|
} catch (error) {
|
|
23058
23517
|
logWarn("[MultiSessionStateManager] Error in state event listener", {
|
|
23059
23518
|
error
|
|
23060
23519
|
});
|
|
23061
23520
|
}
|
|
23062
23521
|
}
|
|
23522
|
+
if (event === "working_state_changed" /* WorkingStateChanged */ && this.sessions.size > this.maxCapacity && this.sessions.get(payload.sessionId)?.getDroidWorkingState() === "idle" /* Idle */) {
|
|
23523
|
+
this.evictIfNeeded();
|
|
23524
|
+
}
|
|
23063
23525
|
};
|
|
23064
23526
|
subscribeToSessionEvent(event, listener) {
|
|
23065
|
-
|
|
23066
|
-
this.sessionStateEvents.on(event, handler);
|
|
23527
|
+
this.sessionStateEvents.on(event, listener);
|
|
23067
23528
|
return () => {
|
|
23068
|
-
this.sessionStateEvents.off(event,
|
|
23529
|
+
this.sessionStateEvents.off(event, listener);
|
|
23069
23530
|
};
|
|
23070
23531
|
}
|
|
23071
23532
|
subscribeToSessionEvents(events, listener) {
|
|
@@ -23148,9 +23609,7 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23148
23609
|
count: this.sessions.size,
|
|
23149
23610
|
limit: this.maxCapacity
|
|
23150
23611
|
});
|
|
23151
|
-
this.
|
|
23152
|
-
this.pendingNotifications.delete(evictionTarget);
|
|
23153
|
-
this.lastAccessTime.delete(evictionTarget);
|
|
23612
|
+
this.evictCachedSession(evictionTarget);
|
|
23154
23613
|
}
|
|
23155
23614
|
}
|
|
23156
23615
|
/**
|
|
@@ -23235,7 +23694,7 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23235
23694
|
* Drain any notifications that arrived for `sessionId` before its session
|
|
23236
23695
|
* manager was registered, replaying them in arrival order.
|
|
23237
23696
|
*/
|
|
23238
|
-
replayPendingNotifications(sessionId,
|
|
23697
|
+
replayPendingNotifications(sessionId, phase) {
|
|
23239
23698
|
const queued = this.pendingNotifications.get(sessionId);
|
|
23240
23699
|
if (!queued || queued.length === 0) return;
|
|
23241
23700
|
logInfo(
|
|
@@ -23245,9 +23704,8 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23245
23704
|
count: queued.length
|
|
23246
23705
|
}
|
|
23247
23706
|
);
|
|
23248
|
-
queued.forEach((params) => {
|
|
23249
|
-
|
|
23250
|
-
this.maybeConfirmOptimisticSubmit(params);
|
|
23707
|
+
queued.forEach(({ params, options }) => {
|
|
23708
|
+
this.handleNotification(params, options);
|
|
23251
23709
|
});
|
|
23252
23710
|
this.pendingNotifications.delete(sessionId);
|
|
23253
23711
|
}
|
|
@@ -23495,7 +23953,7 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23495
23953
|
logInfo("[MultiSessionStateManager] Setting machineId for session", {
|
|
23496
23954
|
sessionId,
|
|
23497
23955
|
machineId,
|
|
23498
|
-
|
|
23956
|
+
state: manager.getMachineId() ?? ""
|
|
23499
23957
|
});
|
|
23500
23958
|
manager.setMachineId(machineId);
|
|
23501
23959
|
this.emitStateChange(sessionId, manager.getLoadState());
|
|
@@ -23520,7 +23978,7 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23520
23978
|
if (previousState !== "LOADED" /* Loaded */) {
|
|
23521
23979
|
this.emitStateChange(sessionId, "LOADED" /* Loaded */);
|
|
23522
23980
|
}
|
|
23523
|
-
this.replayPendingNotifications(sessionId,
|
|
23981
|
+
this.replayPendingNotifications(sessionId, "init");
|
|
23524
23982
|
if (this.sessions.size > this.maxCapacity) {
|
|
23525
23983
|
this.evictIfNeeded();
|
|
23526
23984
|
}
|
|
@@ -23531,16 +23989,17 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23531
23989
|
*/
|
|
23532
23990
|
loadSession(sessionId, machineId, messages, options) {
|
|
23533
23991
|
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
23534
|
-
const { manager
|
|
23535
|
-
sessionId,
|
|
23536
|
-
machineId
|
|
23537
|
-
);
|
|
23992
|
+
const { manager } = this.getOrCreateSessionManager(sessionId, machineId);
|
|
23538
23993
|
const previousState = manager.getLoadState();
|
|
23539
23994
|
const priorMessageIds = new Set(
|
|
23540
23995
|
manager.getMessages().map((message) => message.id)
|
|
23541
23996
|
);
|
|
23542
23997
|
manager.setHasOlderMessages(options?.hasOlderMessages ?? false);
|
|
23543
23998
|
manager.setLoadedMessageLimit(options?.loadedMessageLimit ?? 0);
|
|
23999
|
+
const workingState = options?.workingState;
|
|
24000
|
+
if (workingState !== void 0 && manager.getDroidWorkingState() !== workingState) {
|
|
24001
|
+
manager.hydrateDroidWorkingState(workingState);
|
|
24002
|
+
}
|
|
23544
24003
|
manager.loadSession(sessionId, messages);
|
|
23545
24004
|
if (options && "cwd" in options) {
|
|
23546
24005
|
manager.getStore().setCwd(options.cwd ?? null);
|
|
@@ -23553,8 +24012,8 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23553
24012
|
messages,
|
|
23554
24013
|
priorMessageIds
|
|
23555
24014
|
);
|
|
23556
|
-
this.replayPendingNotifications(sessionId,
|
|
23557
|
-
if (
|
|
24015
|
+
this.replayPendingNotifications(sessionId, "load");
|
|
24016
|
+
if (this.sessions.size > this.maxCapacity) {
|
|
23558
24017
|
this.evictIfNeeded();
|
|
23559
24018
|
}
|
|
23560
24019
|
}
|
|
@@ -23617,7 +24076,7 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23617
24076
|
if (shouldSeedStreaming) {
|
|
23618
24077
|
manager.startStreaming();
|
|
23619
24078
|
}
|
|
23620
|
-
this.replayPendingNotifications(childSessionId,
|
|
24079
|
+
this.replayPendingNotifications(childSessionId, "load");
|
|
23621
24080
|
}
|
|
23622
24081
|
/**
|
|
23623
24082
|
* Get the SessionStateManager for a specific session.
|
|
@@ -23822,10 +24281,42 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23822
24281
|
return this.sessions.get(sessionId)?.getCurrentTodos() ?? null;
|
|
23823
24282
|
}
|
|
23824
24283
|
/**
|
|
23825
|
-
* Remove a session from management
|
|
24284
|
+
* Remove a session from management when it is definitively gone (closed,
|
|
24285
|
+
* not found).
|
|
24286
|
+
*
|
|
24287
|
+
* Pending prompts can never be answered, so they are cleared with
|
|
24288
|
+
* resolution events; the durable session-scoped collections (inactive
|
|
24289
|
+
* flag, concurrent-prompt marker, load options) go with them; and buffered
|
|
24290
|
+
* answers in pendingUserActions are dropped too, because no future load or
|
|
24291
|
+
* SESSION_CLOSED can confirm or drain them once the session is gone.
|
|
24292
|
+
*
|
|
24293
|
+
* Contrast {@link evictCachedSession}, which drops only the LRU cache
|
|
24294
|
+
* entities while the daemon still holds the session.
|
|
23826
24295
|
*/
|
|
23827
24296
|
removeSession(sessionId) {
|
|
23828
24297
|
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24298
|
+
this.evictCachedSession(sessionId);
|
|
24299
|
+
this._askUserRequests.clearSessionAskUserRequests(sessionId);
|
|
24300
|
+
this._permissionRequests.clearSessionPermissions(sessionId, {
|
|
24301
|
+
emitResolved: true
|
|
24302
|
+
});
|
|
24303
|
+
this._pendingUserActions.clearSession(sessionId);
|
|
24304
|
+
this.inactiveSessions.delete(sessionId);
|
|
24305
|
+
this.sessionsWithConcurrentPrompts.delete(sessionId);
|
|
24306
|
+
this.sessionLoadOptions.delete(sessionId);
|
|
24307
|
+
}
|
|
24308
|
+
/**
|
|
24309
|
+
* Drop only the LRU cache entities for a session (render-cache manager,
|
|
24310
|
+
* access time, queued notifications) under cache pressure, leaving the
|
|
24311
|
+
* durable session-scoped state intact: pending prompts, the inactive flag,
|
|
24312
|
+
* the concurrent-prompt marker, stashed load options, and buffered answers
|
|
24313
|
+
* all survive.
|
|
24314
|
+
*
|
|
24315
|
+
* The daemon still holds an evicted session and may be blocked on a pending
|
|
24316
|
+
* prompt, so a later re-open (or a response from another surface) still
|
|
24317
|
+
* resolves it and a self-resume replays the real load flags.
|
|
24318
|
+
*/
|
|
24319
|
+
evictCachedSession(sessionId) {
|
|
23829
24320
|
const session = this.sessions.get(sessionId);
|
|
23830
24321
|
if (session) {
|
|
23831
24322
|
session.getStore().clearAll();
|
|
@@ -23853,6 +24344,12 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23853
24344
|
this.lastAccessTime.clear();
|
|
23854
24345
|
this.subagentInvocationSummaries.clear();
|
|
23855
24346
|
this.preInitSessions.clear();
|
|
24347
|
+
this._askUserRequests.clearAll();
|
|
24348
|
+
this._permissionRequests.clearAllPermissions();
|
|
24349
|
+
this._pendingUserActions.clearAll();
|
|
24350
|
+
this.inactiveSessions.clear();
|
|
24351
|
+
this.sessionsWithConcurrentPrompts.clear();
|
|
24352
|
+
this.sessionLoadOptions.clear();
|
|
23856
24353
|
}
|
|
23857
24354
|
/**
|
|
23858
24355
|
* Handle notifications and route them to the appropriate session.
|
|
@@ -23861,7 +24358,7 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23861
24358
|
* race conditions where notifications arrive before initializeSession/loadSession.
|
|
23862
24359
|
* Queued notifications are replayed after session initialization.
|
|
23863
24360
|
*/
|
|
23864
|
-
handleNotification(params) {
|
|
24361
|
+
handleNotification(params, options) {
|
|
23865
24362
|
const { sessionId } = params;
|
|
23866
24363
|
try {
|
|
23867
24364
|
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
@@ -23883,12 +24380,12 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
23883
24380
|
if (!this.pendingNotifications.has(sessionId)) {
|
|
23884
24381
|
this.pendingNotifications.set(sessionId, []);
|
|
23885
24382
|
}
|
|
23886
|
-
this.pendingNotifications.get(sessionId).push(params);
|
|
24383
|
+
this.pendingNotifications.get(sessionId).push({ params, options });
|
|
23887
24384
|
return;
|
|
23888
24385
|
}
|
|
23889
24386
|
const previousState = manager.getLoadState();
|
|
23890
24387
|
const previousWorkingState = params.notification.type === "droid_working_state_changed" /* DROID_WORKING_STATE_CHANGED */ ? manager.getDroidWorkingState() : void 0;
|
|
23891
|
-
manager.handleNotification(params);
|
|
24388
|
+
manager.handleNotification(params, options);
|
|
23892
24389
|
this.maybeConfirmOptimisticSubmit(params);
|
|
23893
24390
|
const newState = manager.getLoadState();
|
|
23894
24391
|
if (previousState !== newState) {
|
|
@@ -24087,6 +24584,75 @@ var MultiSessionStateManager = class _MultiSessionStateManager {
|
|
|
24087
24584
|
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24088
24585
|
return this.sentInitialPromptSessions.has(sessionId);
|
|
24089
24586
|
}
|
|
24587
|
+
/**
|
|
24588
|
+
* Mark a session's worker as paused (inactivity timeout / process exit).
|
|
24589
|
+
* While inactive, prompt responses are buffered in pendingUserActions and
|
|
24590
|
+
* the owning controller self-resumes instead of firing RPCs at a reaped
|
|
24591
|
+
* worker.
|
|
24592
|
+
*/
|
|
24593
|
+
markSessionInactive(sessionId) {
|
|
24594
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24595
|
+
this.inactiveSessions.add(sessionId);
|
|
24596
|
+
}
|
|
24597
|
+
/**
|
|
24598
|
+
* Clear a session's inactive marker (a successful reload has resumed it).
|
|
24599
|
+
*/
|
|
24600
|
+
markSessionActive(sessionId) {
|
|
24601
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24602
|
+
this.inactiveSessions.delete(sessionId);
|
|
24603
|
+
}
|
|
24604
|
+
/**
|
|
24605
|
+
* Whether a session's worker is currently paused.
|
|
24606
|
+
*/
|
|
24607
|
+
isSessionInactive(sessionId) {
|
|
24608
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24609
|
+
return this.inactiveSessions.has(sessionId);
|
|
24610
|
+
}
|
|
24611
|
+
/**
|
|
24612
|
+
* Mark that a session is draining a batch of concurrent prompts, so a
|
|
24613
|
+
* resume-driven auto-clear does not strand the surviving tail prompt.
|
|
24614
|
+
*/
|
|
24615
|
+
markConcurrentPrompts(sessionId) {
|
|
24616
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24617
|
+
this.sessionsWithConcurrentPrompts.add(sessionId);
|
|
24618
|
+
}
|
|
24619
|
+
/**
|
|
24620
|
+
* Forget a session's concurrent-prompt marker once its batch fully drains.
|
|
24621
|
+
*/
|
|
24622
|
+
clearConcurrentPrompts(sessionId) {
|
|
24623
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24624
|
+
this.sessionsWithConcurrentPrompts.delete(sessionId);
|
|
24625
|
+
}
|
|
24626
|
+
/**
|
|
24627
|
+
* Whether a session is mid-way through draining a concurrent prompt batch.
|
|
24628
|
+
*/
|
|
24629
|
+
hasConcurrentPrompts(sessionId) {
|
|
24630
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24631
|
+
return this.sessionsWithConcurrentPrompts.has(sessionId);
|
|
24632
|
+
}
|
|
24633
|
+
/**
|
|
24634
|
+
* Stash the spawn options used to load a session so they can be replayed on
|
|
24635
|
+
* a later ensureSessionLoaded / self-resume by whichever controller loads
|
|
24636
|
+
* the session next.
|
|
24637
|
+
*/
|
|
24638
|
+
setSessionLoadOptions(sessionId, options) {
|
|
24639
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24640
|
+
this.sessionLoadOptions.set(sessionId, options);
|
|
24641
|
+
}
|
|
24642
|
+
/**
|
|
24643
|
+
* Read the stashed spawn options for a session, if any.
|
|
24644
|
+
*/
|
|
24645
|
+
getSessionLoadOptions(sessionId) {
|
|
24646
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24647
|
+
return this.sessionLoadOptions.get(sessionId);
|
|
24648
|
+
}
|
|
24649
|
+
/**
|
|
24650
|
+
* Drop the stashed spawn options for a session (e.g. on close).
|
|
24651
|
+
*/
|
|
24652
|
+
deleteSessionLoadOptions(sessionId) {
|
|
24653
|
+
_MultiSessionStateManager.validateSessionId(sessionId);
|
|
24654
|
+
this.sessionLoadOptions.delete(sessionId);
|
|
24655
|
+
}
|
|
24090
24656
|
};
|
|
24091
24657
|
|
|
24092
24658
|
// ../daemon-sdk/src/session/DaemonConnectionManager.ts
|
|
@@ -24570,7 +25136,7 @@ var PublicResumeSessionParamsSchema = LoadSessionRequestParamsSchema2.pick({
|
|
|
24570
25136
|
});
|
|
24571
25137
|
function createSdkSessionAttribution(sessionId) {
|
|
24572
25138
|
return {
|
|
24573
|
-
sessionOriginHint: "
|
|
25139
|
+
sessionOriginHint: "sdk" /* Sdk */,
|
|
24574
25140
|
sessionSource: {
|
|
24575
25141
|
platform: "api" /* Api */,
|
|
24576
25142
|
delegationSessionId: sessionId
|
|
@@ -24812,7 +25378,7 @@ var ConnectedDroidSessionImpl = class {
|
|
|
24812
25378
|
},
|
|
24813
25379
|
addUserMessage: (params) => this.controller.addUserMessage(this.id, {
|
|
24814
25380
|
...params,
|
|
24815
|
-
userMessageSource: "
|
|
25381
|
+
userMessageSource: "sdk" /* Sdk */
|
|
24816
25382
|
}),
|
|
24817
25383
|
interruptSession: () => this.controller.interruptSession(this.id)
|
|
24818
25384
|
};
|
|
@@ -24917,7 +25483,7 @@ var SessionsResourceImpl = class {
|
|
|
24917
25483
|
sessionId,
|
|
24918
25484
|
machineId: LOCAL_MACHINE_ID,
|
|
24919
25485
|
token: this.apiKey,
|
|
24920
|
-
tags:
|
|
25486
|
+
tags: withSdkTag(tags),
|
|
24921
25487
|
...createSdkSessionAttribution(sessionId)
|
|
24922
25488
|
});
|
|
24923
25489
|
session.setMetadata(
|
|
@@ -25084,6 +25650,7 @@ async function connectToDaemon(options) {
|
|
|
25084
25650
|
machineType: "local" /* Local */,
|
|
25085
25651
|
url: options.url,
|
|
25086
25652
|
clientType: "sdk",
|
|
25653
|
+
sdk: SDK_CLIENT_METADATA,
|
|
25087
25654
|
getAccessToken,
|
|
25088
25655
|
onAuthenticationError: options.onAuthenticationError,
|
|
25089
25656
|
connectionTimeoutMs: connectionConfig.connectionTimeoutMs,
|
|
@@ -25195,7 +25762,9 @@ async function factoryFetch(path, apiKey, baseUrl, options) {
|
|
|
25195
25762
|
const method = options?.method ?? "GET";
|
|
25196
25763
|
const headers = {
|
|
25197
25764
|
Authorization: `Bearer ${apiKey}`,
|
|
25198
|
-
Accept: "application/json"
|
|
25765
|
+
Accept: "application/json",
|
|
25766
|
+
[FACTORY_CLIENT_HEADER]: "sdk" /* SDK */,
|
|
25767
|
+
[FACTORY_SDK_HEADER]: formatSdkClientMetadata(SDK_CLIENT_METADATA)
|
|
25199
25768
|
};
|
|
25200
25769
|
if (options?.body) {
|
|
25201
25770
|
headers["Content-Type"] = "application/json";
|
|
@@ -25389,6 +25958,7 @@ export {
|
|
|
25389
25958
|
SettingsLevel,
|
|
25390
25959
|
SkillLocation,
|
|
25391
25960
|
JSONRPC_VERSION,
|
|
25961
|
+
formatSdkClientMetadata,
|
|
25392
25962
|
TraceContextMetaSchema,
|
|
25393
25963
|
JsonRpcEnvelopeSchema,
|
|
25394
25964
|
JsonRpcErrorSchema,
|
|
@@ -25679,6 +26249,8 @@ export {
|
|
|
25679
26249
|
FactoryDroidMessageRole,
|
|
25680
26250
|
ServerRequestHandlerType,
|
|
25681
26251
|
SDK_VERSION,
|
|
26252
|
+
SDK_CLIENT_METADATA,
|
|
26253
|
+
SDK_REQUEST_ATTRIBUTION,
|
|
25682
26254
|
SDK_TAG,
|
|
25683
26255
|
JSONRPC_VERSION3,
|
|
25684
26256
|
LEGACY_FACTORY_API_VERSION3 as LEGACY_FACTORY_API_VERSION2,
|
|
@@ -25702,6 +26274,7 @@ export {
|
|
|
25702
26274
|
InvalidSessionCwdError,
|
|
25703
26275
|
ProcessExitError,
|
|
25704
26276
|
mergeSessionSettings,
|
|
26277
|
+
withSdkTag,
|
|
25705
26278
|
unwrapResponseResult,
|
|
25706
26279
|
SessionMetadataSchema,
|
|
25707
26280
|
SessionStartDecompSessionTypeSchema,
|
|
@@ -25828,4 +26401,4 @@ react/cjs/react.development.js:
|
|
|
25828
26401
|
* LICENSE file in the root directory of this source tree.
|
|
25829
26402
|
*)
|
|
25830
26403
|
*/
|
|
25831
|
-
//# sourceMappingURL=chunk-
|
|
26404
|
+
//# sourceMappingURL=chunk-KXDG5ZWS.mjs.map
|