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