@cabane/companion 0.6.118 → 0.6.119
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/cli.js +94 -42
- package/dist/runtime.js +94 -42
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4368,11 +4368,14 @@ var TurnPump = class {
|
|
|
4368
4368
|
},
|
|
4369
4369
|
onToolActivity: async (evt) => {
|
|
4370
4370
|
if (opts.signal.aborted) return;
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4371
|
+
let call = this.toolCallByProviderId.get(evt.toolUseId);
|
|
4372
|
+
if (evt.phase === "start" || call === void 0) {
|
|
4373
|
+
call = { persistedId: crypto.randomUUID(), seq: opts.nextSeq() };
|
|
4374
|
+
this.toolCallByProviderId.set(evt.toolUseId, call);
|
|
4375
|
+
}
|
|
4376
|
+
const { persistedId, seq } = call;
|
|
4374
4377
|
await opts.commit.reportTool({
|
|
4375
|
-
toolUseId:
|
|
4378
|
+
toolUseId: persistedId,
|
|
4376
4379
|
toolName: evt.toolName,
|
|
4377
4380
|
summary: evt.summary,
|
|
4378
4381
|
input: evt.input,
|
|
@@ -4409,9 +4412,19 @@ var TurnPump = class {
|
|
|
4409
4412
|
// The turn's final reply text, captured for the dashboard feed / logging.
|
|
4410
4413
|
finalReplyBody = null;
|
|
4411
4414
|
emittedFinalSource = "none";
|
|
4412
|
-
//
|
|
4413
|
-
//
|
|
4414
|
-
|
|
4415
|
+
// CT1383: provider tool id → the persisted identity of its CURRENT call. The
|
|
4416
|
+
// provider's id is only a lookup key: it is unique within one adapter
|
|
4417
|
+
// invocation at best (Codex restarts at `item_0` on every invocation, and a
|
|
4418
|
+
// resumed turn keeps its turn id), so the row and the outbox entry are keyed
|
|
4419
|
+
// on what the pump mints — a host id for the host's `(turn_id, tool_use_id)`
|
|
4420
|
+
// upsert, and a seq for the outbox's `(turnId, seq)` file. Both are minted
|
|
4421
|
+
// together; minting only the id would let two calls overwrite each other's
|
|
4422
|
+
// queued body.
|
|
4423
|
+
//
|
|
4424
|
+
// In memory on purpose: it dies with the process, and so does the runtime.
|
|
4425
|
+
// After a crash no in-flight `done` awaits a pre-crash `start` — the resumed
|
|
4426
|
+
// invocation starts its calls afresh — so there is nothing to recover.
|
|
4427
|
+
toolCallByProviderId = /* @__PURE__ */ new Map();
|
|
4415
4428
|
// The callback consumer the classification (via `sinkEmitter`) — and the
|
|
4416
4429
|
// frozen committer test — drive. Each callback applies the choreography and
|
|
4417
4430
|
// commits through the injected sink.
|
|
@@ -4806,11 +4819,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4806
4819
|
let usage;
|
|
4807
4820
|
let lastRequestContextTokens;
|
|
4808
4821
|
let resolvedModel;
|
|
4809
|
-
|
|
4810
|
-
let rateLimitResetIso;
|
|
4811
|
-
let rateLimitType;
|
|
4812
|
-
let authError;
|
|
4813
|
-
let lastAssistantError;
|
|
4822
|
+
const signals = { sawRejectedLimit: false };
|
|
4814
4823
|
try {
|
|
4815
4824
|
for await (const msg of iter) {
|
|
4816
4825
|
if (ctx.signal.aborted) return;
|
|
@@ -4833,12 +4842,14 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4833
4842
|
};
|
|
4834
4843
|
}
|
|
4835
4844
|
} else if (!workspaceProven && (msg.type === "assistant" || msg.type === "user" || msg.type === "result")) {
|
|
4836
|
-
|
|
4845
|
+
if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
|
|
4846
|
+
const failure = classifyPreInitFrame(msg, signals);
|
|
4847
|
+
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
4837
4848
|
break;
|
|
4838
4849
|
} else if (msg.type === "assistant") {
|
|
4839
4850
|
const assistantErr = msg.error;
|
|
4840
4851
|
if (typeof assistantErr === "string" && assistantErr.length > 0) {
|
|
4841
|
-
lastAssistantError = assistantErr;
|
|
4852
|
+
signals.lastAssistantError = assistantErr;
|
|
4842
4853
|
}
|
|
4843
4854
|
const reqContext = readRequestContextTokens(msg);
|
|
4844
4855
|
if (reqContext !== void 0) lastRequestContextTokens = reqContext;
|
|
@@ -4850,13 +4861,13 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4850
4861
|
} else if (msg.type === "rate_limit_event") {
|
|
4851
4862
|
const info = msg.rate_limit_info;
|
|
4852
4863
|
if (info?.status === "rejected") {
|
|
4853
|
-
sawRejectedLimit = true;
|
|
4854
|
-
rateLimitResetIso = resetsAtToIso(info.resetsAt) ?? rateLimitResetIso;
|
|
4855
|
-
if (typeof info.rateLimitType === "string") rateLimitType = info.rateLimitType;
|
|
4864
|
+
signals.sawRejectedLimit = true;
|
|
4865
|
+
signals.rateLimitResetIso = resetsAtToIso(info.resetsAt) ?? signals.rateLimitResetIso;
|
|
4866
|
+
if (typeof info.rateLimitType === "string") signals.rateLimitType = info.rateLimitType;
|
|
4856
4867
|
}
|
|
4857
4868
|
} else if (msg.type === "auth_status") {
|
|
4858
4869
|
const err = msg.error;
|
|
4859
|
-
if (typeof err === "string" && err.length > 0) authError = err;
|
|
4870
|
+
if (typeof err === "string" && err.length > 0) signals.authError = err;
|
|
4860
4871
|
} else if (msg.type === "result") {
|
|
4861
4872
|
const origin = msg.origin;
|
|
4862
4873
|
if (origin?.kind === "task-notification") continue;
|
|
@@ -4873,16 +4884,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4873
4884
|
ok = true;
|
|
4874
4885
|
} else {
|
|
4875
4886
|
const resultText = msg.result ?? "";
|
|
4876
|
-
const
|
|
4877
|
-
const errors = msg.errors;
|
|
4878
|
-
const errorText = [resultText, ...Array.isArray(errors) ? errors.map(String) : []].join(
|
|
4879
|
-
" "
|
|
4880
|
-
);
|
|
4881
|
-
const rejectedIsCap = sawRejectedLimit && (rateLimitResetIso !== void 0 || isSubscriptionWindow(rateLimitType));
|
|
4882
|
-
const failure = rejectedIsCap || terminalReason === "blocking_limit" ? {
|
|
4883
|
-
kind: "usage_capped",
|
|
4884
|
-
...rateLimitResetIso ? { resetsAt: rateLimitResetIso } : {}
|
|
4885
|
-
} : classifyAssistantError(lastAssistantError) ?? classifyErrorText([authError, errorText].filter(Boolean).join(" "));
|
|
4887
|
+
const failure = classifyResultFailure(msg, signals);
|
|
4886
4888
|
resultReason = failure ? encodeFailureReason(failure) : isError ? `error:${resultText.slice(0, 200) || "unknown"}` : `result_error:${msg.subtype}`;
|
|
4887
4889
|
ok = false;
|
|
4888
4890
|
}
|
|
@@ -4891,16 +4893,25 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4891
4893
|
}
|
|
4892
4894
|
} catch (err) {
|
|
4893
4895
|
if (ctx.signal.aborted) throw err;
|
|
4894
|
-
const
|
|
4895
|
-
|
|
4896
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4897
|
+
const failure = mcpInventory.initReceived ? classifyErrorText(message) : classifyFailure(signals, { text: message });
|
|
4898
|
+
if (!failure && (mcpInventory.initReceived || ctx.resumedSessionId === null)) throw err;
|
|
4896
4899
|
ok = false;
|
|
4897
|
-
|
|
4900
|
+
if (failure) {
|
|
4901
|
+
resultReason = encodeFailureReason(failure);
|
|
4902
|
+
} else {
|
|
4903
|
+
resultReason = "session_start_failed";
|
|
4904
|
+
if (ctx.startEvidence) ctx.startEvidence.thrown = message.slice(0, 200);
|
|
4905
|
+
}
|
|
4898
4906
|
sawResult = true;
|
|
4899
4907
|
}
|
|
4900
4908
|
if (ctx.signal.aborted) return;
|
|
4901
4909
|
if (!workspaceProven) {
|
|
4902
4910
|
ok = false;
|
|
4903
|
-
resultReason
|
|
4911
|
+
if (resultReason === void 0) {
|
|
4912
|
+
const failure = classifyFailure(signals, { text: "" });
|
|
4913
|
+
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
4914
|
+
}
|
|
4904
4915
|
}
|
|
4905
4916
|
const heldFailure = !ok ? classifyRuntimeNoticeText(buffer.held) : null;
|
|
4906
4917
|
const terminalFailure = !ok ? decodeFailureReason(resultReason) : null;
|
|
@@ -4935,6 +4946,42 @@ function provesWorkspaceTools(msg) {
|
|
|
4935
4946
|
const tools = msg.tools;
|
|
4936
4947
|
return Array.isArray(tools) && tools.includes("mcp__cabane__query");
|
|
4937
4948
|
}
|
|
4949
|
+
function classifyFailure(s, frame) {
|
|
4950
|
+
const rejectedIsCap = s.sawRejectedLimit && (s.rateLimitResetIso !== void 0 || isSubscriptionWindow(s.rateLimitType));
|
|
4951
|
+
if (rejectedIsCap || frame.terminalReason === "blocking_limit")
|
|
4952
|
+
return {
|
|
4953
|
+
kind: "usage_capped",
|
|
4954
|
+
...s.rateLimitResetIso ? { resetsAt: s.rateLimitResetIso } : {}
|
|
4955
|
+
};
|
|
4956
|
+
return classifyAssistantError(s.lastAssistantError) ?? classifyErrorText([s.authError, frame.text].filter(Boolean).join(" "));
|
|
4957
|
+
}
|
|
4958
|
+
function classifyResultFailure(msg, s) {
|
|
4959
|
+
const resultText = msg.result ?? "";
|
|
4960
|
+
const terminalReason = msg.terminal_reason;
|
|
4961
|
+
const errors = msg.errors;
|
|
4962
|
+
const text = [resultText, ...Array.isArray(errors) ? errors.map(String) : []].join(" ");
|
|
4963
|
+
return classifyFailure(s, { terminalReason, text });
|
|
4964
|
+
}
|
|
4965
|
+
function classifyPreInitFrame(msg, s) {
|
|
4966
|
+
if (msg.type === "result") {
|
|
4967
|
+
const isError = msg.is_error === true;
|
|
4968
|
+
if (msg.subtype === "success" && !isError) return classifyFailure(s, { text: "" });
|
|
4969
|
+
return classifyResultFailure(msg, s);
|
|
4970
|
+
}
|
|
4971
|
+
if (msg.type !== "assistant") return classifyFailure(s, { text: "" });
|
|
4972
|
+
const error = msg.error;
|
|
4973
|
+
const signals = typeof error === "string" && error.length > 0 ? { ...s, lastAssistantError: error } : s;
|
|
4974
|
+
const text = contentBlocks(msg).flatMap((b) => b.type === "text" && typeof b.text === "string" ? [b.text] : []).join(" ");
|
|
4975
|
+
return classifyFailure(signals, { text });
|
|
4976
|
+
}
|
|
4977
|
+
function preInitToolActivity(msg) {
|
|
4978
|
+
const kind = msg.type === "assistant" ? "tool_use" : msg.type === "user" ? "tool_result" : null;
|
|
4979
|
+
return kind !== null && contentBlocks(msg).some((b) => b.type === kind);
|
|
4980
|
+
}
|
|
4981
|
+
function contentBlocks(msg) {
|
|
4982
|
+
const content = msg.message?.content;
|
|
4983
|
+
return Array.isArray(content) ? content : [];
|
|
4984
|
+
}
|
|
4938
4985
|
function isSubscriptionWindow(value) {
|
|
4939
4986
|
return value === "five_hour" || value === "seven_day" || value === "seven_day_opus" || value === "seven_day_sonnet" || value === "overage";
|
|
4940
4987
|
}
|
|
@@ -4994,15 +5041,18 @@ function attachAbortController(options, signal) {
|
|
|
4994
5041
|
else signal.addEventListener("abort", () => sdkAbort.abort(), { once: true });
|
|
4995
5042
|
options.abortController = sdkAbort;
|
|
4996
5043
|
}
|
|
4997
|
-
function sdkEvents(queryFn, req, signal, built, degraded) {
|
|
5044
|
+
function sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
|
|
4998
5045
|
attachAbortController(built.options, signal);
|
|
4999
5046
|
const prompt = buildQueryPrompt(req);
|
|
5000
|
-
const iter =
|
|
5047
|
+
const iter = {
|
|
5048
|
+
[Symbol.asyncIterator]: () => queryFn({ prompt, options: built.options })[Symbol.asyncIterator]()
|
|
5049
|
+
};
|
|
5001
5050
|
return decodeSdkStream(iter, {
|
|
5002
5051
|
signal,
|
|
5003
5052
|
cwd: req.local.cwd,
|
|
5004
5053
|
resumedSessionId: built.resume,
|
|
5005
|
-
degraded
|
|
5054
|
+
degraded,
|
|
5055
|
+
...startEvidence ? { startEvidence } : {}
|
|
5006
5056
|
});
|
|
5007
5057
|
}
|
|
5008
5058
|
async function freshRequest(req, reason, hooks) {
|
|
@@ -5011,8 +5061,9 @@ async function freshRequest(req, reason, hooks) {
|
|
|
5011
5061
|
}
|
|
5012
5062
|
async function* runWithResumeRecovery(queryFn, req, signal, initial, augmentOptions, onWarn, degraded, hooks) {
|
|
5013
5063
|
let emitted = false;
|
|
5014
|
-
|
|
5015
|
-
|
|
5064
|
+
const evidence = { toolActivity: false };
|
|
5065
|
+
for await (const event of sdkEvents(queryFn, req, signal, initial, degraded, evidence)) {
|
|
5066
|
+
const recoverResume = initial.resume !== null && !emitted && !evidence.toolActivity && event.type === "result" && !event.ok && event.reason === "session_start_failed" && event.mcpInventory?.initReceived === false;
|
|
5016
5067
|
if (recoverResume) {
|
|
5017
5068
|
if (signal.aborted) return;
|
|
5018
5069
|
onWarn?.(
|
|
@@ -5020,7 +5071,8 @@ async function* runWithResumeRecovery(queryFn, req, signal, initial, augmentOpti
|
|
|
5020
5071
|
{
|
|
5021
5072
|
cwd: req.local.cwd,
|
|
5022
5073
|
reason: "session_start_failed",
|
|
5023
|
-
discardedSessionId: initial.resume
|
|
5074
|
+
discardedSessionId: initial.resume,
|
|
5075
|
+
...evidence.thrown ? { thrown: evidence.thrown } : {}
|
|
5024
5076
|
}
|
|
5025
5077
|
);
|
|
5026
5078
|
const fresh = await freshRequest(req, "session_start_failed", hooks);
|
|
@@ -9925,13 +9977,13 @@ var TurnExecution = class {
|
|
|
9925
9977
|
} else {
|
|
9926
9978
|
const delayMs = this.opts.preparingRowDelayMs ?? DEFAULT_PREPARING_ROW_DELAY_MS;
|
|
9927
9979
|
let preparingStarted = false;
|
|
9928
|
-
let
|
|
9980
|
+
let preparing = null;
|
|
9929
9981
|
const reportPreparing = (phase) => {
|
|
9930
|
-
|
|
9931
|
-
const seq =
|
|
9982
|
+
preparing ??= { id: `prepare:${randomUUID3()}`, seq: this.nextSeq() };
|
|
9983
|
+
const { id, seq } = preparing;
|
|
9932
9984
|
void this.opts.api.reportActivity(workspaceId, payload.conversationId, payload.agentId, {
|
|
9933
9985
|
turnId,
|
|
9934
|
-
toolUseId:
|
|
9986
|
+
toolUseId: id,
|
|
9935
9987
|
toolName: PREPARING_TOOL_NAME,
|
|
9936
9988
|
summary: "",
|
|
9937
9989
|
phase,
|
package/dist/runtime.js
CHANGED
|
@@ -3781,11 +3781,14 @@ var TurnPump = class {
|
|
|
3781
3781
|
},
|
|
3782
3782
|
onToolActivity: async (evt) => {
|
|
3783
3783
|
if (opts.signal.aborted) return;
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3784
|
+
let call = this.toolCallByProviderId.get(evt.toolUseId);
|
|
3785
|
+
if (evt.phase === "start" || call === void 0) {
|
|
3786
|
+
call = { persistedId: crypto.randomUUID(), seq: opts.nextSeq() };
|
|
3787
|
+
this.toolCallByProviderId.set(evt.toolUseId, call);
|
|
3788
|
+
}
|
|
3789
|
+
const { persistedId, seq } = call;
|
|
3787
3790
|
await opts.commit.reportTool({
|
|
3788
|
-
toolUseId:
|
|
3791
|
+
toolUseId: persistedId,
|
|
3789
3792
|
toolName: evt.toolName,
|
|
3790
3793
|
summary: evt.summary,
|
|
3791
3794
|
input: evt.input,
|
|
@@ -3822,9 +3825,19 @@ var TurnPump = class {
|
|
|
3822
3825
|
// The turn's final reply text, captured for the dashboard feed / logging.
|
|
3823
3826
|
finalReplyBody = null;
|
|
3824
3827
|
emittedFinalSource = "none";
|
|
3825
|
-
//
|
|
3826
|
-
//
|
|
3827
|
-
|
|
3828
|
+
// CT1383: provider tool id → the persisted identity of its CURRENT call. The
|
|
3829
|
+
// provider's id is only a lookup key: it is unique within one adapter
|
|
3830
|
+
// invocation at best (Codex restarts at `item_0` on every invocation, and a
|
|
3831
|
+
// resumed turn keeps its turn id), so the row and the outbox entry are keyed
|
|
3832
|
+
// on what the pump mints — a host id for the host's `(turn_id, tool_use_id)`
|
|
3833
|
+
// upsert, and a seq for the outbox's `(turnId, seq)` file. Both are minted
|
|
3834
|
+
// together; minting only the id would let two calls overwrite each other's
|
|
3835
|
+
// queued body.
|
|
3836
|
+
//
|
|
3837
|
+
// In memory on purpose: it dies with the process, and so does the runtime.
|
|
3838
|
+
// After a crash no in-flight `done` awaits a pre-crash `start` — the resumed
|
|
3839
|
+
// invocation starts its calls afresh — so there is nothing to recover.
|
|
3840
|
+
toolCallByProviderId = /* @__PURE__ */ new Map();
|
|
3828
3841
|
// The callback consumer the classification (via `sinkEmitter`) — and the
|
|
3829
3842
|
// frozen committer test — drive. Each callback applies the choreography and
|
|
3830
3843
|
// commits through the injected sink.
|
|
@@ -4219,11 +4232,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4219
4232
|
let usage;
|
|
4220
4233
|
let lastRequestContextTokens;
|
|
4221
4234
|
let resolvedModel;
|
|
4222
|
-
|
|
4223
|
-
let rateLimitResetIso;
|
|
4224
|
-
let rateLimitType;
|
|
4225
|
-
let authError;
|
|
4226
|
-
let lastAssistantError;
|
|
4235
|
+
const signals = { sawRejectedLimit: false };
|
|
4227
4236
|
try {
|
|
4228
4237
|
for await (const msg of iter) {
|
|
4229
4238
|
if (ctx.signal.aborted) return;
|
|
@@ -4246,12 +4255,14 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4246
4255
|
};
|
|
4247
4256
|
}
|
|
4248
4257
|
} else if (!workspaceProven && (msg.type === "assistant" || msg.type === "user" || msg.type === "result")) {
|
|
4249
|
-
|
|
4258
|
+
if (preInitToolActivity(msg) && ctx.startEvidence) ctx.startEvidence.toolActivity = true;
|
|
4259
|
+
const failure = classifyPreInitFrame(msg, signals);
|
|
4260
|
+
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
4250
4261
|
break;
|
|
4251
4262
|
} else if (msg.type === "assistant") {
|
|
4252
4263
|
const assistantErr = msg.error;
|
|
4253
4264
|
if (typeof assistantErr === "string" && assistantErr.length > 0) {
|
|
4254
|
-
lastAssistantError = assistantErr;
|
|
4265
|
+
signals.lastAssistantError = assistantErr;
|
|
4255
4266
|
}
|
|
4256
4267
|
const reqContext = readRequestContextTokens(msg);
|
|
4257
4268
|
if (reqContext !== void 0) lastRequestContextTokens = reqContext;
|
|
@@ -4263,13 +4274,13 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4263
4274
|
} else if (msg.type === "rate_limit_event") {
|
|
4264
4275
|
const info = msg.rate_limit_info;
|
|
4265
4276
|
if (info?.status === "rejected") {
|
|
4266
|
-
sawRejectedLimit = true;
|
|
4267
|
-
rateLimitResetIso = resetsAtToIso(info.resetsAt) ?? rateLimitResetIso;
|
|
4268
|
-
if (typeof info.rateLimitType === "string") rateLimitType = info.rateLimitType;
|
|
4277
|
+
signals.sawRejectedLimit = true;
|
|
4278
|
+
signals.rateLimitResetIso = resetsAtToIso(info.resetsAt) ?? signals.rateLimitResetIso;
|
|
4279
|
+
if (typeof info.rateLimitType === "string") signals.rateLimitType = info.rateLimitType;
|
|
4269
4280
|
}
|
|
4270
4281
|
} else if (msg.type === "auth_status") {
|
|
4271
4282
|
const err = msg.error;
|
|
4272
|
-
if (typeof err === "string" && err.length > 0) authError = err;
|
|
4283
|
+
if (typeof err === "string" && err.length > 0) signals.authError = err;
|
|
4273
4284
|
} else if (msg.type === "result") {
|
|
4274
4285
|
const origin = msg.origin;
|
|
4275
4286
|
if (origin?.kind === "task-notification") continue;
|
|
@@ -4286,16 +4297,7 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4286
4297
|
ok = true;
|
|
4287
4298
|
} else {
|
|
4288
4299
|
const resultText = msg.result ?? "";
|
|
4289
|
-
const
|
|
4290
|
-
const errors = msg.errors;
|
|
4291
|
-
const errorText = [resultText, ...Array.isArray(errors) ? errors.map(String) : []].join(
|
|
4292
|
-
" "
|
|
4293
|
-
);
|
|
4294
|
-
const rejectedIsCap = sawRejectedLimit && (rateLimitResetIso !== void 0 || isSubscriptionWindow(rateLimitType));
|
|
4295
|
-
const failure = rejectedIsCap || terminalReason === "blocking_limit" ? {
|
|
4296
|
-
kind: "usage_capped",
|
|
4297
|
-
...rateLimitResetIso ? { resetsAt: rateLimitResetIso } : {}
|
|
4298
|
-
} : classifyAssistantError(lastAssistantError) ?? classifyErrorText([authError, errorText].filter(Boolean).join(" "));
|
|
4300
|
+
const failure = classifyResultFailure(msg, signals);
|
|
4299
4301
|
resultReason = failure ? encodeFailureReason(failure) : isError ? `error:${resultText.slice(0, 200) || "unknown"}` : `result_error:${msg.subtype}`;
|
|
4300
4302
|
ok = false;
|
|
4301
4303
|
}
|
|
@@ -4304,16 +4306,25 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4304
4306
|
}
|
|
4305
4307
|
} catch (err) {
|
|
4306
4308
|
if (ctx.signal.aborted) throw err;
|
|
4307
|
-
const
|
|
4308
|
-
|
|
4309
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4310
|
+
const failure = mcpInventory.initReceived ? classifyErrorText(message) : classifyFailure(signals, { text: message });
|
|
4311
|
+
if (!failure && (mcpInventory.initReceived || ctx.resumedSessionId === null)) throw err;
|
|
4309
4312
|
ok = false;
|
|
4310
|
-
|
|
4313
|
+
if (failure) {
|
|
4314
|
+
resultReason = encodeFailureReason(failure);
|
|
4315
|
+
} else {
|
|
4316
|
+
resultReason = "session_start_failed";
|
|
4317
|
+
if (ctx.startEvidence) ctx.startEvidence.thrown = message.slice(0, 200);
|
|
4318
|
+
}
|
|
4311
4319
|
sawResult = true;
|
|
4312
4320
|
}
|
|
4313
4321
|
if (ctx.signal.aborted) return;
|
|
4314
4322
|
if (!workspaceProven) {
|
|
4315
4323
|
ok = false;
|
|
4316
|
-
resultReason
|
|
4324
|
+
if (resultReason === void 0) {
|
|
4325
|
+
const failure = classifyFailure(signals, { text: "" });
|
|
4326
|
+
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
4327
|
+
}
|
|
4317
4328
|
}
|
|
4318
4329
|
const heldFailure = !ok ? classifyRuntimeNoticeText(buffer.held) : null;
|
|
4319
4330
|
const terminalFailure = !ok ? decodeFailureReason(resultReason) : null;
|
|
@@ -4348,6 +4359,42 @@ function provesWorkspaceTools(msg) {
|
|
|
4348
4359
|
const tools = msg.tools;
|
|
4349
4360
|
return Array.isArray(tools) && tools.includes("mcp__cabane__query");
|
|
4350
4361
|
}
|
|
4362
|
+
function classifyFailure(s, frame) {
|
|
4363
|
+
const rejectedIsCap = s.sawRejectedLimit && (s.rateLimitResetIso !== void 0 || isSubscriptionWindow(s.rateLimitType));
|
|
4364
|
+
if (rejectedIsCap || frame.terminalReason === "blocking_limit")
|
|
4365
|
+
return {
|
|
4366
|
+
kind: "usage_capped",
|
|
4367
|
+
...s.rateLimitResetIso ? { resetsAt: s.rateLimitResetIso } : {}
|
|
4368
|
+
};
|
|
4369
|
+
return classifyAssistantError(s.lastAssistantError) ?? classifyErrorText([s.authError, frame.text].filter(Boolean).join(" "));
|
|
4370
|
+
}
|
|
4371
|
+
function classifyResultFailure(msg, s) {
|
|
4372
|
+
const resultText = msg.result ?? "";
|
|
4373
|
+
const terminalReason = msg.terminal_reason;
|
|
4374
|
+
const errors = msg.errors;
|
|
4375
|
+
const text = [resultText, ...Array.isArray(errors) ? errors.map(String) : []].join(" ");
|
|
4376
|
+
return classifyFailure(s, { terminalReason, text });
|
|
4377
|
+
}
|
|
4378
|
+
function classifyPreInitFrame(msg, s) {
|
|
4379
|
+
if (msg.type === "result") {
|
|
4380
|
+
const isError = msg.is_error === true;
|
|
4381
|
+
if (msg.subtype === "success" && !isError) return classifyFailure(s, { text: "" });
|
|
4382
|
+
return classifyResultFailure(msg, s);
|
|
4383
|
+
}
|
|
4384
|
+
if (msg.type !== "assistant") return classifyFailure(s, { text: "" });
|
|
4385
|
+
const error = msg.error;
|
|
4386
|
+
const signals = typeof error === "string" && error.length > 0 ? { ...s, lastAssistantError: error } : s;
|
|
4387
|
+
const text = contentBlocks(msg).flatMap((b) => b.type === "text" && typeof b.text === "string" ? [b.text] : []).join(" ");
|
|
4388
|
+
return classifyFailure(signals, { text });
|
|
4389
|
+
}
|
|
4390
|
+
function preInitToolActivity(msg) {
|
|
4391
|
+
const kind = msg.type === "assistant" ? "tool_use" : msg.type === "user" ? "tool_result" : null;
|
|
4392
|
+
return kind !== null && contentBlocks(msg).some((b) => b.type === kind);
|
|
4393
|
+
}
|
|
4394
|
+
function contentBlocks(msg) {
|
|
4395
|
+
const content = msg.message?.content;
|
|
4396
|
+
return Array.isArray(content) ? content : [];
|
|
4397
|
+
}
|
|
4351
4398
|
function isSubscriptionWindow(value) {
|
|
4352
4399
|
return value === "five_hour" || value === "seven_day" || value === "seven_day_opus" || value === "seven_day_sonnet" || value === "overage";
|
|
4353
4400
|
}
|
|
@@ -4407,15 +4454,18 @@ function attachAbortController(options, signal) {
|
|
|
4407
4454
|
else signal.addEventListener("abort", () => sdkAbort.abort(), { once: true });
|
|
4408
4455
|
options.abortController = sdkAbort;
|
|
4409
4456
|
}
|
|
4410
|
-
function sdkEvents(queryFn, req, signal, built, degraded) {
|
|
4457
|
+
function sdkEvents(queryFn, req, signal, built, degraded, startEvidence) {
|
|
4411
4458
|
attachAbortController(built.options, signal);
|
|
4412
4459
|
const prompt = buildQueryPrompt(req);
|
|
4413
|
-
const iter =
|
|
4460
|
+
const iter = {
|
|
4461
|
+
[Symbol.asyncIterator]: () => queryFn({ prompt, options: built.options })[Symbol.asyncIterator]()
|
|
4462
|
+
};
|
|
4414
4463
|
return decodeSdkStream(iter, {
|
|
4415
4464
|
signal,
|
|
4416
4465
|
cwd: req.local.cwd,
|
|
4417
4466
|
resumedSessionId: built.resume,
|
|
4418
|
-
degraded
|
|
4467
|
+
degraded,
|
|
4468
|
+
...startEvidence ? { startEvidence } : {}
|
|
4419
4469
|
});
|
|
4420
4470
|
}
|
|
4421
4471
|
async function freshRequest(req, reason, hooks) {
|
|
@@ -4424,8 +4474,9 @@ async function freshRequest(req, reason, hooks) {
|
|
|
4424
4474
|
}
|
|
4425
4475
|
async function* runWithResumeRecovery(queryFn, req, signal, initial, augmentOptions, onWarn, degraded, hooks) {
|
|
4426
4476
|
let emitted = false;
|
|
4427
|
-
|
|
4428
|
-
|
|
4477
|
+
const evidence = { toolActivity: false };
|
|
4478
|
+
for await (const event of sdkEvents(queryFn, req, signal, initial, degraded, evidence)) {
|
|
4479
|
+
const recoverResume = initial.resume !== null && !emitted && !evidence.toolActivity && event.type === "result" && !event.ok && event.reason === "session_start_failed" && event.mcpInventory?.initReceived === false;
|
|
4429
4480
|
if (recoverResume) {
|
|
4430
4481
|
if (signal.aborted) return;
|
|
4431
4482
|
onWarn?.(
|
|
@@ -4433,7 +4484,8 @@ async function* runWithResumeRecovery(queryFn, req, signal, initial, augmentOpti
|
|
|
4433
4484
|
{
|
|
4434
4485
|
cwd: req.local.cwd,
|
|
4435
4486
|
reason: "session_start_failed",
|
|
4436
|
-
discardedSessionId: initial.resume
|
|
4487
|
+
discardedSessionId: initial.resume,
|
|
4488
|
+
...evidence.thrown ? { thrown: evidence.thrown } : {}
|
|
4437
4489
|
}
|
|
4438
4490
|
);
|
|
4439
4491
|
const fresh = await freshRequest(req, "session_start_failed", hooks);
|
|
@@ -9348,13 +9400,13 @@ var TurnExecution = class {
|
|
|
9348
9400
|
} else {
|
|
9349
9401
|
const delayMs = this.opts.preparingRowDelayMs ?? DEFAULT_PREPARING_ROW_DELAY_MS;
|
|
9350
9402
|
let preparingStarted = false;
|
|
9351
|
-
let
|
|
9403
|
+
let preparing = null;
|
|
9352
9404
|
const reportPreparing = (phase) => {
|
|
9353
|
-
|
|
9354
|
-
const seq =
|
|
9405
|
+
preparing ??= { id: `prepare:${randomUUID3()}`, seq: this.nextSeq() };
|
|
9406
|
+
const { id, seq } = preparing;
|
|
9355
9407
|
void this.opts.api.reportActivity(workspaceId, payload.conversationId, payload.agentId, {
|
|
9356
9408
|
turnId,
|
|
9357
|
-
toolUseId:
|
|
9409
|
+
toolUseId: id,
|
|
9358
9410
|
toolName: PREPARING_TOOL_NAME,
|
|
9359
9411
|
summary: "",
|
|
9360
9412
|
phase,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.119",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect a coding agent on your machine to your Cabane workspace as a responder — drive work against your own codebase, files, and MCP servers without putting any of it in Cabane.",
|
|
6
6
|
"license": "UNLICENSED",
|