@m8t-stack/cli 0.2.81 → 0.2.83
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 +523 -63
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -229,10 +229,77 @@ var init_agent_roster = __esm({
|
|
|
229
229
|
});
|
|
230
230
|
|
|
231
231
|
// ../../packages/api-contract/dist/esm/ui-directive.js
|
|
232
|
-
|
|
232
|
+
function parseArgs(input) {
|
|
233
|
+
if (!isRecord(input) || !nonEmpty(input.title) || !Array.isArray(input.options))
|
|
234
|
+
return null;
|
|
235
|
+
const { options } = input;
|
|
236
|
+
if (options.length < MIN_OPTIONS || options.length > MAX_OPTIONS)
|
|
237
|
+
return null;
|
|
238
|
+
const parsed = [];
|
|
239
|
+
for (const option of options) {
|
|
240
|
+
if (!isRecord(option) || !nonEmpty(option.label) || !nonEmpty(option.detail))
|
|
241
|
+
return null;
|
|
242
|
+
parsed.push({ label: option.label, detail: option.detail });
|
|
243
|
+
}
|
|
244
|
+
return { title: input.title, options: parsed };
|
|
245
|
+
}
|
|
246
|
+
function parseState(input, optionCount) {
|
|
247
|
+
if (!isRecord(input))
|
|
248
|
+
return null;
|
|
249
|
+
switch (input.status) {
|
|
250
|
+
case "pending":
|
|
251
|
+
return { status: "pending" };
|
|
252
|
+
case "dismissed":
|
|
253
|
+
return { status: "dismissed" };
|
|
254
|
+
case "submitting":
|
|
255
|
+
return inRange(input.optionIndex, optionCount) ? { status: "submitting", optionIndex: input.optionIndex } : null;
|
|
256
|
+
case "selected":
|
|
257
|
+
if (!inRange(input.optionIndex, optionCount))
|
|
258
|
+
return null;
|
|
259
|
+
return input.acknowledgement === "pending" || input.acknowledgement === "complete" || input.acknowledgement === "error" ? { status: "selected", optionIndex: input.optionIndex, acknowledgement: input.acknowledgement } : null;
|
|
260
|
+
case "error":
|
|
261
|
+
return nonEmpty(input.message) ? { status: "error", message: input.message } : null;
|
|
262
|
+
default:
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function parseUiDirectiveFrame(input) {
|
|
267
|
+
if (!isRecord(input))
|
|
268
|
+
return null;
|
|
269
|
+
if (input.source !== "voice" && input.source !== "text")
|
|
270
|
+
return null;
|
|
271
|
+
const directive = input.directive;
|
|
272
|
+
if (!isRecord(directive))
|
|
273
|
+
return null;
|
|
274
|
+
if (directive.version !== 1 || directive.name !== "present_decision")
|
|
275
|
+
return null;
|
|
276
|
+
if (!nonEmpty(directive.callId))
|
|
277
|
+
return null;
|
|
278
|
+
const args = parseArgs(directive.args);
|
|
279
|
+
if (!args)
|
|
280
|
+
return null;
|
|
281
|
+
const state = parseState(directive.state, args.options.length);
|
|
282
|
+
if (!state)
|
|
283
|
+
return null;
|
|
284
|
+
return {
|
|
285
|
+
directive: { version: 1, name: "present_decision", callId: directive.callId, args, state },
|
|
286
|
+
source: input.source
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function isDecisionRefusalReason(value) {
|
|
290
|
+
return typeof value === "string" && decisionRefusalReasons.includes(value);
|
|
291
|
+
}
|
|
292
|
+
var UI_DIRECTIVE_PART_TYPE, MIN_OPTIONS, MAX_OPTIONS, isRecord, nonEmpty, inRange, UI_DIRECTIVE_RESULT_PART_TYPE, DECISION_REFUSAL_REASONS, decisionRefusalReasons;
|
|
233
293
|
var init_ui_directive = __esm({
|
|
234
294
|
"../../packages/api-contract/dist/esm/ui-directive.js"() {
|
|
235
295
|
"use strict";
|
|
296
|
+
UI_DIRECTIVE_PART_TYPE = "data-ui-directive";
|
|
297
|
+
MIN_OPTIONS = 2;
|
|
298
|
+
MAX_OPTIONS = 4;
|
|
299
|
+
isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
300
|
+
nonEmpty = (value) => typeof value === "string" && value.trim().length > 0;
|
|
301
|
+
inRange = (value, count) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value < count;
|
|
302
|
+
UI_DIRECTIVE_RESULT_PART_TYPE = "data-ui-result";
|
|
236
303
|
DECISION_REFUSAL_REASONS = {
|
|
237
304
|
/** 409 — no live call with that `callId`. Answered twice, or talked past and dismissed. */
|
|
238
305
|
notPending: "decision_not_pending",
|
|
@@ -1400,7 +1467,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1400
1467
|
import { Builtins, Cli } from "clipanion";
|
|
1401
1468
|
|
|
1402
1469
|
// src/lib/package-version.ts
|
|
1403
|
-
var CLI_VERSION = "0.2.
|
|
1470
|
+
var CLI_VERSION = "0.2.83";
|
|
1404
1471
|
|
|
1405
1472
|
// src/lib/render-error.ts
|
|
1406
1473
|
init_errors();
|
|
@@ -12728,6 +12795,25 @@ function classifyFoundryError(err) {
|
|
|
12728
12795
|
return { category: "unknown", retryable: false, status, message };
|
|
12729
12796
|
}
|
|
12730
12797
|
|
|
12798
|
+
// ../../packages/foundry-invoke/dist/esm/artifacts.js
|
|
12799
|
+
var FENCE_RE = /```json m8t:artifacts\s*\n([\s\S]*?)\n```/;
|
|
12800
|
+
function parseArtifacts(raw) {
|
|
12801
|
+
const text = raw;
|
|
12802
|
+
const m = FENCE_RE.exec(text);
|
|
12803
|
+
if (!m)
|
|
12804
|
+
return { text: text.trimEnd(), artifacts: [] };
|
|
12805
|
+
let artifacts = [];
|
|
12806
|
+
try {
|
|
12807
|
+
const parsed = JSON.parse(m[1]);
|
|
12808
|
+
if (Array.isArray(parsed))
|
|
12809
|
+
artifacts = parsed;
|
|
12810
|
+
} catch {
|
|
12811
|
+
artifacts = [];
|
|
12812
|
+
}
|
|
12813
|
+
const clean2 = text.slice(0, m.index).trimEnd();
|
|
12814
|
+
return { text: clean2, artifacts };
|
|
12815
|
+
}
|
|
12816
|
+
|
|
12731
12817
|
// src/lib/render-error.ts
|
|
12732
12818
|
function renderHint(hint) {
|
|
12733
12819
|
return hint.split("\n").join("\n ");
|
|
@@ -26708,7 +26794,7 @@ var str3 = (v) => {
|
|
|
26708
26794
|
return "";
|
|
26709
26795
|
};
|
|
26710
26796
|
var req = (v, fallback = "") => str3(v) ?? fallback;
|
|
26711
|
-
function
|
|
26797
|
+
function parseArtifacts2(v) {
|
|
26712
26798
|
if (typeof v !== "string" || v === "")
|
|
26713
26799
|
return void 0;
|
|
26714
26800
|
try {
|
|
@@ -26742,7 +26828,7 @@ function mapEntity(e) {
|
|
|
26742
26828
|
delegationId: str3(e.delegationId),
|
|
26743
26829
|
parentEventId: str3(e.parentEventId),
|
|
26744
26830
|
depth: e.depth == null ? void 0 : Number(e.depth),
|
|
26745
|
-
artifacts:
|
|
26831
|
+
artifacts: parseArtifacts2(e.artifacts)
|
|
26746
26832
|
};
|
|
26747
26833
|
}
|
|
26748
26834
|
var rank = (o) => o === "ok" ? 0 : 1;
|
|
@@ -28806,21 +28892,28 @@ var ARM4 = "https://management.azure.com";
|
|
|
28806
28892
|
var ARM_SCOPE8 = "https://management.azure.com/.default";
|
|
28807
28893
|
var STORAGE_API2 = "2023-05-01";
|
|
28808
28894
|
var LA_API = "2023-09-01";
|
|
28895
|
+
var tagOf = (a, key2) => a.tags?.[key2] ?? a.properties?.tags?.[key2];
|
|
28896
|
+
function selectLedgerAccount(accounts, storageAccount) {
|
|
28897
|
+
if (storageAccount) return accounts.find((a) => a.name === storageAccount);
|
|
28898
|
+
const managed = accounts.filter((a) => tagOf(a, "managedBy") === "m8t");
|
|
28899
|
+
return managed.find((a) => tagOf(a, "m8t") === "storage") ?? managed.at(0) ?? accounts.find((a) => tagOf(a, "m8t:role") === "ledger") ?? (accounts.length === 1 ? accounts[0] : void 0);
|
|
28900
|
+
}
|
|
28809
28901
|
async function discoverLedgerResources(opts) {
|
|
28810
|
-
const { credential: credential2, subscriptionId, resourceGroup } = opts;
|
|
28902
|
+
const { credential: credential2, subscriptionId, resourceGroup, storageAccount } = opts;
|
|
28811
28903
|
const storage = await authedJsonPagedValues({
|
|
28812
28904
|
credential: credential2,
|
|
28813
28905
|
scope: ARM_SCOPE8,
|
|
28814
28906
|
url: `${ARM4}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Storage/storageAccounts?api-version=${STORAGE_API2}`
|
|
28815
28907
|
});
|
|
28816
28908
|
const accounts = storage.filter((a) => a.properties?.primaryEndpoints?.table);
|
|
28817
|
-
const ledgerAcct =
|
|
28909
|
+
const ledgerAcct = selectLedgerAccount(accounts, storageAccount);
|
|
28818
28910
|
const ledgerTableEndpoint = ledgerAcct?.properties?.primaryEndpoints?.table?.replace(/\/$/, "");
|
|
28819
28911
|
if (!ledgerTableEndpoint) {
|
|
28912
|
+
const found = accounts.map((a) => a.name).join(", ") || "(none)";
|
|
28820
28913
|
throw new LocalCliError({
|
|
28821
28914
|
code: "DREAM_LEDGER_STORAGE_NOT_FOUND",
|
|
28822
|
-
message: `No AgentLedger storage account found in resource group ${resourceGroup}.`,
|
|
28823
|
-
hint: `Found: ${
|
|
28915
|
+
message: storageAccount ? `Storage account '${storageAccount}' was not found in resource group ${resourceGroup}, or has no table endpoint.` : `No AgentLedger storage account found in resource group ${resourceGroup}.`,
|
|
28916
|
+
hint: `Found: ${found}. Pass --storage-account <name> to select one explicitly.`
|
|
28824
28917
|
});
|
|
28825
28918
|
}
|
|
28826
28919
|
const ws = await authedJsonPagedValues({
|
|
@@ -28862,6 +28955,7 @@ async function resolveDreamContext(opts) {
|
|
|
28862
28955
|
});
|
|
28863
28956
|
const resourceGroup = rgFromScope(project.accountScope);
|
|
28864
28957
|
const { ledgerTableEndpoint, workspaceId } = await discoverLedgerResources({
|
|
28958
|
+
storageAccount: opts.storageAccount,
|
|
28865
28959
|
credential: credential2,
|
|
28866
28960
|
subscriptionId,
|
|
28867
28961
|
resourceGroup
|
|
@@ -28929,6 +29023,9 @@ var DreamRunCommand = class extends M8tCommand {
|
|
|
28929
29023
|
});
|
|
28930
29024
|
subscription = Option49.String("--subscription");
|
|
28931
29025
|
endpoint = Option49.String("--endpoint");
|
|
29026
|
+
storageAccount = Option49.String("--storage-account", {
|
|
29027
|
+
description: "Ledger storage account name (skips tag-based discovery)"
|
|
29028
|
+
});
|
|
28932
29029
|
output = Option49.String("--output");
|
|
28933
29030
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
28934
29031
|
deps;
|
|
@@ -28946,7 +29043,8 @@ var DreamRunCommand = class extends M8tCommand {
|
|
|
28946
29043
|
const liveContext = await liveDeps.resolveContext({
|
|
28947
29044
|
worker,
|
|
28948
29045
|
subscription: typeof this.subscription === "string" ? this.subscription : void 0,
|
|
28949
|
-
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
|
|
29046
|
+
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
29047
|
+
storageAccount: typeof this.storageAccount === "string" ? this.storageAccount : void 0
|
|
28950
29048
|
});
|
|
28951
29049
|
const liveCursor = await liveDeps.readCursor(liveContext.worker);
|
|
28952
29050
|
const result2 = await liveDeps.runDream({
|
|
@@ -28993,7 +29091,8 @@ var DreamRunCommand = class extends M8tCommand {
|
|
|
28993
29091
|
const context = await deps.resolveContext({
|
|
28994
29092
|
worker,
|
|
28995
29093
|
subscription: typeof this.subscription === "string" ? this.subscription : void 0,
|
|
28996
|
-
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
|
|
29094
|
+
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
29095
|
+
storageAccount: typeof this.storageAccount === "string" ? this.storageAccount : void 0
|
|
28997
29096
|
});
|
|
28998
29097
|
const now = /* @__PURE__ */ new Date();
|
|
28999
29098
|
const cursor = await deps.readCursor(context.worker);
|
|
@@ -29343,13 +29442,28 @@ function defaultDeps2() {
|
|
|
29343
29442
|
const { ledgerTableEndpoint } = await discoverLedgerResources({
|
|
29344
29443
|
credential: credential2,
|
|
29345
29444
|
subscriptionId,
|
|
29346
|
-
resourceGroup: m[1]
|
|
29445
|
+
resourceGroup: m[1],
|
|
29446
|
+
storageAccount: opts.storageAccount
|
|
29347
29447
|
});
|
|
29348
29448
|
return { projectEndpoint: project.endpoint, ledgerTableEndpoint };
|
|
29349
29449
|
},
|
|
29350
29450
|
async fetchRows(ctx, fromIso, toIso) {
|
|
29351
29451
|
const client = new TableClient8(ctx.ledgerTableEndpoint, LEDGER_TABLE_NAME2, credential2);
|
|
29352
|
-
|
|
29452
|
+
try {
|
|
29453
|
+
return await fetchLedgerRows({ workers: [], source: "all", from: fromIso, to: toIso }, client);
|
|
29454
|
+
} catch (e) {
|
|
29455
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
29456
|
+
if (statusOf2(e) === 403 || msg.includes("AuthorizationPermissionMismatch")) {
|
|
29457
|
+
const account = /https:\/\/([^.]+)\./.exec(ctx.ledgerTableEndpoint)?.[1] ?? "the ledger account";
|
|
29458
|
+
throw new LocalCliError({
|
|
29459
|
+
code: "sweep_ledger_forbidden",
|
|
29460
|
+
message: `Not authorized to read the ledger table on '${account}'.`,
|
|
29461
|
+
hint: `Reading table rows needs a data-plane role: grant 'Storage Table Data Reader' on that storage account to the signed-in identity (owning the account is not enough).`,
|
|
29462
|
+
cause: e
|
|
29463
|
+
});
|
|
29464
|
+
}
|
|
29465
|
+
throw e;
|
|
29466
|
+
}
|
|
29353
29467
|
},
|
|
29354
29468
|
async retrieveMetadata(ctx, conversationId) {
|
|
29355
29469
|
try {
|
|
@@ -29420,6 +29534,9 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
29420
29534
|
});
|
|
29421
29535
|
subscription = Option50.String("--subscription", { description: "Azure subscription id override" });
|
|
29422
29536
|
endpoint = Option50.String("--endpoint", { description: "Foundry project endpoint override" });
|
|
29537
|
+
storageAccount = Option50.String("--storage-account", {
|
|
29538
|
+
description: "Ledger storage account name (skips tag-based discovery)"
|
|
29539
|
+
});
|
|
29423
29540
|
deps = defaultDeps2();
|
|
29424
29541
|
async executeCommand() {
|
|
29425
29542
|
const out = this.context.stdout;
|
|
@@ -29436,7 +29553,11 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
29436
29553
|
const cutoffIso = new Date(now - retentionDays * DAY_MS).toISOString();
|
|
29437
29554
|
const fromIso = new Date(now - lookback * DAY_MS).toISOString();
|
|
29438
29555
|
const toIso = new Date(now).toISOString();
|
|
29439
|
-
const ctx = await this.deps.resolveContext({
|
|
29556
|
+
const ctx = await this.deps.resolveContext({
|
|
29557
|
+
subscription: this.subscription,
|
|
29558
|
+
endpoint: this.endpoint,
|
|
29559
|
+
storageAccount: this.storageAccount
|
|
29560
|
+
});
|
|
29440
29561
|
out.write(`Scanning ledger activity ${fromIso} \u2192 ${toIso}; retention cutoff ${cutoffIso}.
|
|
29441
29562
|
`);
|
|
29442
29563
|
const rows = await this.deps.fetchRows(ctx, fromIso, toIso);
|
|
@@ -30728,7 +30849,7 @@ var ONBOARDING_BLOCK_KEYS = [
|
|
|
30728
30849
|
"advisor_name",
|
|
30729
30850
|
"advisor_email"
|
|
30730
30851
|
];
|
|
30731
|
-
function
|
|
30852
|
+
function isRecord2(value) {
|
|
30732
30853
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30733
30854
|
}
|
|
30734
30855
|
function hasValidUniqueJsonKeys(source) {
|
|
@@ -30836,7 +30957,7 @@ var V3_COMPANY_KEYS = ["name", "one_liner"];
|
|
|
30836
30957
|
var V3_ADDRESS_KEYS = ["address", "city", "postal_code", "country"];
|
|
30837
30958
|
var V3_REQUEST_KEYS = ["type", "model", "region", "consent", "company_address"];
|
|
30838
30959
|
function exactStringRecord(value, keys) {
|
|
30839
|
-
if (!
|
|
30960
|
+
if (!isRecord2(value)) return "non-string-value";
|
|
30840
30961
|
const present = new Set(Object.keys(value));
|
|
30841
30962
|
for (const key2 of keys) if (!present.has(key2)) return "missing-key";
|
|
30842
30963
|
if (present.size !== keys.length) return "unexpected-key";
|
|
@@ -30848,7 +30969,7 @@ function canonicalPendingRequests(value) {
|
|
|
30848
30969
|
if (value.length > 1) return { ok: false, reason: "too-many-pending-requests" };
|
|
30849
30970
|
const requests = [];
|
|
30850
30971
|
for (const entry of value) {
|
|
30851
|
-
if (!
|
|
30972
|
+
if (!isRecord2(entry)) return { ok: false, reason: "non-string-value" };
|
|
30852
30973
|
const present = new Set(Object.keys(entry));
|
|
30853
30974
|
for (const key2 of V3_REQUEST_KEYS) {
|
|
30854
30975
|
if (!present.has(key2)) return { ok: false, reason: "missing-key" };
|
|
@@ -30898,7 +31019,7 @@ function canonicalV3Block(value) {
|
|
|
30898
31019
|
};
|
|
30899
31020
|
}
|
|
30900
31021
|
function canonicalBlock(value) {
|
|
30901
|
-
if (!
|
|
31022
|
+
if (!isRecord2(value)) return { ok: false, reason: "non-string-value" };
|
|
30902
31023
|
if (value.schema_version === "3") return canonicalV3Block(value);
|
|
30903
31024
|
if (value.schema_version !== "2") return { ok: false, reason: "unknown-schema-version" };
|
|
30904
31025
|
const keys = new Set(Object.keys(value));
|
|
@@ -30943,7 +31064,7 @@ function parseOnboardingArtifactResult(machineText) {
|
|
|
30943
31064
|
if (json.includes("m8t_onboarding")) return { ok: false, reason: "malformed-json" };
|
|
30944
31065
|
continue;
|
|
30945
31066
|
}
|
|
30946
|
-
if (!
|
|
31067
|
+
if (!isRecord2(parsed) || !Object.hasOwn(parsed, "m8t_onboarding")) continue;
|
|
30947
31068
|
if (Object.keys(parsed).length !== 1) return { ok: false, reason: "unexpected-key" };
|
|
30948
31069
|
const outcome = canonicalBlock(parsed.m8t_onboarding);
|
|
30949
31070
|
if (!outcome.ok) return outcome;
|
|
@@ -30989,7 +31110,7 @@ async function readCursorPages(args) {
|
|
|
30989
31110
|
} catch {
|
|
30990
31111
|
return null;
|
|
30991
31112
|
}
|
|
30992
|
-
if (!
|
|
31113
|
+
if (!isRecord2(body) || !Array.isArray(body.data)) return null;
|
|
30993
31114
|
const page = body;
|
|
30994
31115
|
all.push(...page.data);
|
|
30995
31116
|
if (page.has_more !== true) {
|
|
@@ -31010,7 +31131,7 @@ function decodeFoundryUser(token) {
|
|
|
31010
31131
|
if (parts.length !== 3 || parts.some((part) => part.length === 0) || !/^[A-Za-z0-9_-]+$/.test(parts[1] ?? "")) return null;
|
|
31011
31132
|
try {
|
|
31012
31133
|
const payload = JSON.parse(Buffer.from(parts[1] ?? "", "base64url").toString("utf8"));
|
|
31013
|
-
if (!
|
|
31134
|
+
if (!isRecord2(payload)) return null;
|
|
31014
31135
|
const claim = payload.upn ?? payload.preferred_username ?? payload.email;
|
|
31015
31136
|
return typeof claim === "string" && claim.trim().length > 0 ? claim : null;
|
|
31016
31137
|
} catch {
|
|
@@ -31041,7 +31162,7 @@ async function findOnboardingProfile(args) {
|
|
|
31041
31162
|
});
|
|
31042
31163
|
if (!listed) return { ...EMPTY_PROFILE_RESULT };
|
|
31043
31164
|
const conversations = orderNewest(listed.flatMap((value, ordinal) => {
|
|
31044
|
-
if (!
|
|
31165
|
+
if (!isRecord2(value) || typeof value.id !== "string" || value.id.length === 0 || !isRecord2(value.metadata)) return [];
|
|
31045
31166
|
const metadata = value.metadata;
|
|
31046
31167
|
if (metadata.app !== "m8t-webapp" || metadata.agent !== INTAKE_AGENT_NAME) return [];
|
|
31047
31168
|
return [{
|
|
@@ -31059,7 +31180,7 @@ async function findOnboardingProfile(args) {
|
|
|
31059
31180
|
});
|
|
31060
31181
|
if (!items) return { hadIntake: true, block: null, machineText: null, speechText: null, rejection: null };
|
|
31061
31182
|
const assistantItems = orderNewest(items.flatMap((value, ordinal) => {
|
|
31062
|
-
if (!
|
|
31183
|
+
if (!isRecord2(value) || value.type !== "message" || value.role !== "assistant" || !Array.isArray(value.content)) return [];
|
|
31063
31184
|
const id = typeof value.id === "string" ? value.id : "";
|
|
31064
31185
|
return [{ value, id, createdAt: timestamp(value.created_at), ordinal }];
|
|
31065
31186
|
}));
|
|
@@ -31068,7 +31189,7 @@ async function findOnboardingProfile(args) {
|
|
|
31068
31189
|
for (const item of assistantItems) {
|
|
31069
31190
|
const content = item.value.content;
|
|
31070
31191
|
const machineText = content.flatMap((part) => {
|
|
31071
|
-
if (!
|
|
31192
|
+
if (!isRecord2(part)) return [];
|
|
31072
31193
|
if (typeof part.text === "string" && part.text.length > 0) return [part.text];
|
|
31073
31194
|
if (typeof part.transcript === "string") return [part.transcript];
|
|
31074
31195
|
return [];
|
|
@@ -34571,6 +34692,14 @@ var COMPANION_HISTORY_LIMIT_MAX = 40;
|
|
|
34571
34692
|
var COMPANION_TURN_TEXT_MAX_CODE_POINTS = 32768;
|
|
34572
34693
|
var COMPANION_REPLY_DELTA_MAX_CODE_POINTS = 4096;
|
|
34573
34694
|
var COMPANION_TURN_ID_MAX_LENGTH = 256;
|
|
34695
|
+
var COMPANION_DECISION_CALL_ID_MAX_LENGTH = 256;
|
|
34696
|
+
var COMPANION_DECISION_TITLE_MAX_LENGTH = 200;
|
|
34697
|
+
var COMPANION_DECISION_LABEL_MAX_LENGTH = 100;
|
|
34698
|
+
var COMPANION_DECISION_DETAIL_MAX_CODE_POINTS = 400;
|
|
34699
|
+
var COMPANION_DECISION_OPTIONS_MIN = 2;
|
|
34700
|
+
var COMPANION_DECISION_OPTIONS_MAX = 4;
|
|
34701
|
+
var COMPANION_ARTIFACT_NAME_MAX_LENGTH = 200;
|
|
34702
|
+
var COMPANION_ARTIFACTS_MAX = 16;
|
|
34574
34703
|
var PERSONAS = /* @__PURE__ */ new Set([
|
|
34575
34704
|
"startup-advisor",
|
|
34576
34705
|
"ezra"
|
|
@@ -34580,7 +34709,8 @@ var FAILURE_REASONS = /* @__PURE__ */ new Set([
|
|
|
34580
34709
|
"not-authorized",
|
|
34581
34710
|
"mate-unavailable",
|
|
34582
34711
|
"busy",
|
|
34583
|
-
"bad-request"
|
|
34712
|
+
"bad-request",
|
|
34713
|
+
"decision-not-pending"
|
|
34584
34714
|
]);
|
|
34585
34715
|
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/u;
|
|
34586
34716
|
var URL_SAFE_OPAQUE_ID = /^[A-Za-z0-9_-]+$/u;
|
|
@@ -34589,7 +34719,7 @@ var VERSION3 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/u;
|
|
|
34589
34719
|
function isVersionOrNull(value) {
|
|
34590
34720
|
return value === null || typeof value === "string" && VERSION3.test(value);
|
|
34591
34721
|
}
|
|
34592
|
-
function
|
|
34722
|
+
function isRecord3(value) {
|
|
34593
34723
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34594
34724
|
}
|
|
34595
34725
|
function hasExactKeys(value, keys) {
|
|
@@ -34613,8 +34743,72 @@ var INSTANT_SHAPE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
|
|
|
34613
34743
|
function isInstantOrNull(value) {
|
|
34614
34744
|
return value === null || typeof value === "string" && INSTANT_SHAPE.test(value) && Number.isFinite(Date.parse(value));
|
|
34615
34745
|
}
|
|
34616
|
-
function
|
|
34617
|
-
|
|
34746
|
+
function parseDecision(value) {
|
|
34747
|
+
if (!isRecord3(value) || !isBoundedPlainString(value.callId, COMPANION_DECISION_CALL_ID_MAX_LENGTH) || !isBoundedPlainString(value.title, COMPANION_DECISION_TITLE_MAX_LENGTH) || !Array.isArray(value.options) || value.options.length < COMPANION_DECISION_OPTIONS_MIN || value.options.length > COMPANION_DECISION_OPTIONS_MAX) {
|
|
34748
|
+
return eventError();
|
|
34749
|
+
}
|
|
34750
|
+
const options = value.options.map((option) => {
|
|
34751
|
+
if (!isRecord3(option) || !hasExactKeys(option, ["label", "detail"]) || !isBoundedPlainString(option.label, COMPANION_DECISION_LABEL_MAX_LENGTH) || !isBoundedMessageText(option.detail, COMPANION_DECISION_DETAIL_MAX_CODE_POINTS)) {
|
|
34752
|
+
return eventError();
|
|
34753
|
+
}
|
|
34754
|
+
return { label: option.label, detail: option.detail };
|
|
34755
|
+
});
|
|
34756
|
+
const base = { callId: value.callId, title: value.title, options };
|
|
34757
|
+
if (value.status === "pending" || value.status === "dismissed") {
|
|
34758
|
+
if (!hasExactKeys(value, ["callId", "title", "options", "status"])) {
|
|
34759
|
+
return eventError();
|
|
34760
|
+
}
|
|
34761
|
+
return { ...base, status: value.status };
|
|
34762
|
+
}
|
|
34763
|
+
if (value.status !== "selected" || !hasExactKeys(value, ["callId", "title", "options", "status", "optionIndex"]) || !Number.isSafeInteger(value.optionIndex) || value.optionIndex < 0 || value.optionIndex >= options.length) {
|
|
34764
|
+
return eventError();
|
|
34765
|
+
}
|
|
34766
|
+
return { ...base, status: "selected", optionIndex: value.optionIndex };
|
|
34767
|
+
}
|
|
34768
|
+
function parseArtifact(value) {
|
|
34769
|
+
if (!isRecord3(value) || !isBoundedPlainString(value.name, COMPANION_ARTIFACT_NAME_MAX_LENGTH)) {
|
|
34770
|
+
return eventError();
|
|
34771
|
+
}
|
|
34772
|
+
if (value.sizeBytes === void 0) {
|
|
34773
|
+
if (!hasExactKeys(value, ["name"])) return eventError();
|
|
34774
|
+
return { name: value.name };
|
|
34775
|
+
}
|
|
34776
|
+
if (!hasExactKeys(value, ["name", "sizeBytes"]) || !Number.isSafeInteger(value.sizeBytes) || value.sizeBytes < 0) {
|
|
34777
|
+
return eventError();
|
|
34778
|
+
}
|
|
34779
|
+
return { name: value.name, sizeBytes: value.sizeBytes };
|
|
34780
|
+
}
|
|
34781
|
+
function parseTurnArtifacts(value) {
|
|
34782
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > COMPANION_ARTIFACTS_MAX) {
|
|
34783
|
+
return eventError();
|
|
34784
|
+
}
|
|
34785
|
+
return value.map(parseArtifact);
|
|
34786
|
+
}
|
|
34787
|
+
function parseTurn(value) {
|
|
34788
|
+
if (!isRecord3(value) || !isBoundedPlainString(value.id, COMPANION_TURN_ID_MAX_LENGTH) || value.role !== "user" && value.role !== "mate" || !isInstantOrNull(value.at)) {
|
|
34789
|
+
return eventError();
|
|
34790
|
+
}
|
|
34791
|
+
const keys = [
|
|
34792
|
+
"id",
|
|
34793
|
+
"role",
|
|
34794
|
+
"text",
|
|
34795
|
+
"at",
|
|
34796
|
+
...value.decision === void 0 ? [] : ["decision"],
|
|
34797
|
+
...value.artifacts === void 0 ? [] : ["artifacts"]
|
|
34798
|
+
];
|
|
34799
|
+
if (!hasExactKeys(value, keys)) return eventError();
|
|
34800
|
+
const allowEmptyText = value.decision !== void 0 || value.artifacts !== void 0;
|
|
34801
|
+
if (!(allowEmptyText && value.text === "" || isBoundedMessageText(value.text, COMPANION_TURN_TEXT_MAX_CODE_POINTS))) {
|
|
34802
|
+
return eventError();
|
|
34803
|
+
}
|
|
34804
|
+
return {
|
|
34805
|
+
id: value.id,
|
|
34806
|
+
role: value.role,
|
|
34807
|
+
text: value.text,
|
|
34808
|
+
at: value.at,
|
|
34809
|
+
...value.decision === void 0 ? {} : { decision: parseDecision(value.decision) },
|
|
34810
|
+
...value.artifacts === void 0 ? {} : { artifacts: parseTurnArtifacts(value.artifacts) }
|
|
34811
|
+
};
|
|
34618
34812
|
}
|
|
34619
34813
|
function isCanonicalInstant(value) {
|
|
34620
34814
|
if (typeof value !== "string") return false;
|
|
@@ -34670,7 +34864,7 @@ function isValidMateRoute(value) {
|
|
|
34670
34864
|
}
|
|
34671
34865
|
}
|
|
34672
34866
|
function parseRequest(value) {
|
|
34673
|
-
if (!
|
|
34867
|
+
if (!isRecord3(value)) return requestError();
|
|
34674
34868
|
if (value.type === "roster") {
|
|
34675
34869
|
if (!hasExactKeys(value, ["type"])) return requestError();
|
|
34676
34870
|
return { type: "roster" };
|
|
@@ -34679,18 +34873,36 @@ function parseRequest(value) {
|
|
|
34679
34873
|
if (!hasExactKeys(value, ["type"])) return requestError();
|
|
34680
34874
|
return { type: "update" };
|
|
34681
34875
|
}
|
|
34682
|
-
if (value.type === "history") {
|
|
34876
|
+
if (value.type === "history" || value.type === "recall") {
|
|
34683
34877
|
if (!hasExactKeys(value, ["type", "requestId", "personaKey", "limit"]) || !isRequestId(value.requestId) || !isPersonaKey(value.personaKey) || !Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > COMPANION_HISTORY_LIMIT_MAX) {
|
|
34684
34878
|
return requestError();
|
|
34685
34879
|
}
|
|
34686
34880
|
return {
|
|
34687
|
-
type:
|
|
34881
|
+
type: value.type,
|
|
34688
34882
|
requestId: value.requestId,
|
|
34689
34883
|
personaKey: value.personaKey,
|
|
34690
34884
|
limit: value.limit
|
|
34691
34885
|
};
|
|
34692
34886
|
}
|
|
34693
|
-
if (value.type
|
|
34887
|
+
if (value.type === "decide") {
|
|
34888
|
+
if (!hasExactKeys(value, [
|
|
34889
|
+
"type",
|
|
34890
|
+
"requestId",
|
|
34891
|
+
"personaKey",
|
|
34892
|
+
"callId",
|
|
34893
|
+
"optionIndex"
|
|
34894
|
+
]) || !isRequestId(value.requestId) || !isPersonaKey(value.personaKey) || !isBoundedPlainString(value.callId, COMPANION_DECISION_CALL_ID_MAX_LENGTH) || !Number.isSafeInteger(value.optionIndex) || value.optionIndex < 0 || value.optionIndex >= COMPANION_DECISION_OPTIONS_MAX) {
|
|
34895
|
+
return requestError();
|
|
34896
|
+
}
|
|
34897
|
+
return {
|
|
34898
|
+
type: "decide",
|
|
34899
|
+
requestId: value.requestId,
|
|
34900
|
+
personaKey: value.personaKey,
|
|
34901
|
+
callId: value.callId,
|
|
34902
|
+
optionIndex: value.optionIndex
|
|
34903
|
+
};
|
|
34904
|
+
}
|
|
34905
|
+
if (value.type !== "send" && value.type !== "converse" && value.type !== "chat" || !hasExactKeys(value, [
|
|
34694
34906
|
"type",
|
|
34695
34907
|
"requestId",
|
|
34696
34908
|
"personaKey",
|
|
@@ -34718,7 +34930,7 @@ function parseRequestLine(line2) {
|
|
|
34718
34930
|
}
|
|
34719
34931
|
}
|
|
34720
34932
|
function parseMate(value) {
|
|
34721
|
-
if (!
|
|
34933
|
+
if (!isRecord3(value) || !hasExactKeys(value, [
|
|
34722
34934
|
"personaKey",
|
|
34723
34935
|
"agentName",
|
|
34724
34936
|
"displayName",
|
|
@@ -34743,7 +34955,7 @@ function parseMate(value) {
|
|
|
34743
34955
|
};
|
|
34744
34956
|
}
|
|
34745
34957
|
function parseEvent(value) {
|
|
34746
|
-
if (!
|
|
34958
|
+
if (!isRecord3(value)) return eventError();
|
|
34747
34959
|
if (value.type === "update") {
|
|
34748
34960
|
if (!hasExactKeys(value, ["type", "installed", "available", "severity"]) || !isVersionOrNull(value.installed) || !isVersionOrNull(value.available) || !(value.severity === null || SEVERITIES2.has(value.severity))) {
|
|
34749
34961
|
return eventError();
|
|
@@ -34786,18 +34998,33 @@ function parseEvent(value) {
|
|
|
34786
34998
|
return { type: "completed", requestId: value.requestId };
|
|
34787
34999
|
}
|
|
34788
35000
|
if (value.type === "turn") {
|
|
34789
|
-
if (!hasExactKeys(value, ["type", "requestId", "turn"]) || !isRequestId(value.requestId)
|
|
35001
|
+
if (!hasExactKeys(value, ["type", "requestId", "turn"]) || !isRequestId(value.requestId)) {
|
|
34790
35002
|
return eventError();
|
|
34791
35003
|
}
|
|
34792
35004
|
return {
|
|
34793
35005
|
type: "turn",
|
|
34794
35006
|
requestId: value.requestId,
|
|
34795
|
-
turn:
|
|
34796
|
-
|
|
34797
|
-
|
|
34798
|
-
|
|
34799
|
-
|
|
34800
|
-
|
|
35007
|
+
turn: parseTurn(value.turn)
|
|
35008
|
+
};
|
|
35009
|
+
}
|
|
35010
|
+
if (value.type === "reply-decision") {
|
|
35011
|
+
if (!hasExactKeys(value, ["type", "requestId", "decision"]) || !isRequestId(value.requestId)) {
|
|
35012
|
+
return eventError();
|
|
35013
|
+
}
|
|
35014
|
+
return {
|
|
35015
|
+
type: "reply-decision",
|
|
35016
|
+
requestId: value.requestId,
|
|
35017
|
+
decision: parseDecision(value.decision)
|
|
35018
|
+
};
|
|
35019
|
+
}
|
|
35020
|
+
if (value.type === "reply-artifacts") {
|
|
35021
|
+
if (!hasExactKeys(value, ["type", "requestId", "artifacts"]) || !isRequestId(value.requestId) || !Array.isArray(value.artifacts) || value.artifacts.length === 0 || value.artifacts.length > COMPANION_ARTIFACTS_MAX) {
|
|
35022
|
+
return eventError();
|
|
35023
|
+
}
|
|
35024
|
+
return {
|
|
35025
|
+
type: "reply-artifacts",
|
|
35026
|
+
requestId: value.requestId,
|
|
35027
|
+
artifacts: value.artifacts.map(parseArtifact)
|
|
34801
35028
|
};
|
|
34802
35029
|
}
|
|
34803
35030
|
if (value.type === "history-end") {
|
|
@@ -34854,6 +35081,7 @@ function serializeEvent(value) {
|
|
|
34854
35081
|
}
|
|
34855
35082
|
|
|
34856
35083
|
// src/lib/companion-chat-client.ts
|
|
35084
|
+
init_esm();
|
|
34857
35085
|
import { stat as stat4 } from "fs/promises";
|
|
34858
35086
|
var RESPONSE_MAX_BYTES = 1e6;
|
|
34859
35087
|
var REQUEST_DEADLINE_MS = 12e4;
|
|
@@ -34866,7 +35094,7 @@ var PredispatchFailure = class extends Error {
|
|
|
34866
35094
|
}
|
|
34867
35095
|
reason;
|
|
34868
35096
|
};
|
|
34869
|
-
function
|
|
35097
|
+
function isRecord4(value) {
|
|
34870
35098
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34871
35099
|
}
|
|
34872
35100
|
function hasControlCharacter(value) {
|
|
@@ -34936,16 +35164,16 @@ async function readJsonEnvelope(response) {
|
|
|
34936
35164
|
if (error instanceof PredispatchFailure) throw error;
|
|
34937
35165
|
throw new PredispatchFailure("not-connected");
|
|
34938
35166
|
}
|
|
34939
|
-
if (!
|
|
35167
|
+
if (!isRecord4(envelope) || envelope.ok !== true || !("data" in envelope)) {
|
|
34940
35168
|
throw new PredispatchFailure("not-connected");
|
|
34941
35169
|
}
|
|
34942
35170
|
return envelope.data;
|
|
34943
35171
|
}
|
|
34944
35172
|
function decodeAgents(data) {
|
|
34945
|
-
if (!
|
|
35173
|
+
if (!isRecord4(data) || !Array.isArray(data.agents)) {
|
|
34946
35174
|
throw new PredispatchFailure("mate-unavailable");
|
|
34947
35175
|
}
|
|
34948
|
-
return data.agents.filter(
|
|
35176
|
+
return data.agents.filter(isRecord4);
|
|
34949
35177
|
}
|
|
34950
35178
|
function resolveAgentName(agents, personaKey) {
|
|
34951
35179
|
const matches = agents.filter(
|
|
@@ -34967,18 +35195,18 @@ function requireAgentName(agents, personaKey) {
|
|
|
34967
35195
|
return name;
|
|
34968
35196
|
}
|
|
34969
35197
|
function decodeConversationId(data) {
|
|
34970
|
-
if (!
|
|
35198
|
+
if (!isRecord4(data) || typeof data.id !== "string" || !OPAQUE_ID.test(data.id)) {
|
|
34971
35199
|
throw new PredispatchFailure("not-connected");
|
|
34972
35200
|
}
|
|
34973
35201
|
return data.id;
|
|
34974
35202
|
}
|
|
34975
35203
|
function decodeLastMessageId(data) {
|
|
34976
|
-
if (!
|
|
35204
|
+
if (!isRecord4(data) || !Array.isArray(data.messages)) {
|
|
34977
35205
|
throw new PredispatchFailure("not-connected");
|
|
34978
35206
|
}
|
|
34979
35207
|
const messages = data.messages;
|
|
34980
35208
|
const last = messages.at(-1);
|
|
34981
|
-
if (!
|
|
35209
|
+
if (!isRecord4(last) || typeof last.id !== "string" || !OPAQUE_ID.test(last.id)) {
|
|
34982
35210
|
return void 0;
|
|
34983
35211
|
}
|
|
34984
35212
|
return last.id;
|
|
@@ -35029,13 +35257,61 @@ function boundCodePoints(value, maxCodePoints) {
|
|
|
35029
35257
|
}
|
|
35030
35258
|
return value;
|
|
35031
35259
|
}
|
|
35260
|
+
function boundPlainLine(value, maxLength) {
|
|
35261
|
+
return value.replace(/[\u0000-\u001f\u007f]/gu, " ").trim().slice(0, maxLength);
|
|
35262
|
+
}
|
|
35263
|
+
var ARTIFACTS_PART_TYPE = "data-artifacts";
|
|
35264
|
+
function toCompanionDecision(directive) {
|
|
35265
|
+
const state = directive.state;
|
|
35266
|
+
if (state.status !== "pending" && state.status !== "selected" && state.status !== "dismissed") {
|
|
35267
|
+
return null;
|
|
35268
|
+
}
|
|
35269
|
+
const callId = boundPlainLine(
|
|
35270
|
+
directive.callId,
|
|
35271
|
+
COMPANION_DECISION_CALL_ID_MAX_LENGTH
|
|
35272
|
+
);
|
|
35273
|
+
const title = boundPlainLine(
|
|
35274
|
+
directive.args.title,
|
|
35275
|
+
COMPANION_DECISION_TITLE_MAX_LENGTH
|
|
35276
|
+
);
|
|
35277
|
+
if (callId.length === 0 || title.length === 0) return null;
|
|
35278
|
+
const options = [];
|
|
35279
|
+
for (const option of directive.args.options) {
|
|
35280
|
+
const label = boundPlainLine(option.label, COMPANION_DECISION_LABEL_MAX_LENGTH);
|
|
35281
|
+
const detail = boundCodePoints(
|
|
35282
|
+
sanitizeMessageText(option.detail).trim(),
|
|
35283
|
+
COMPANION_DECISION_DETAIL_MAX_CODE_POINTS
|
|
35284
|
+
);
|
|
35285
|
+
if (label.length === 0 || detail.length === 0) return null;
|
|
35286
|
+
options.push({ label, detail });
|
|
35287
|
+
}
|
|
35288
|
+
const base = { callId, title, options };
|
|
35289
|
+
return state.status === "selected" ? { ...base, status: "selected", optionIndex: state.optionIndex } : { ...base, status: state.status };
|
|
35290
|
+
}
|
|
35291
|
+
function toCompanionArtifacts(data) {
|
|
35292
|
+
const artifacts = [];
|
|
35293
|
+
for (const entry of data) {
|
|
35294
|
+
if (artifacts.length >= COMPANION_ARTIFACTS_MAX) break;
|
|
35295
|
+
if (!isRecord4(entry) || typeof entry.name !== "string") continue;
|
|
35296
|
+
const name = boundPlainLine(entry.name, COMPANION_ARTIFACT_NAME_MAX_LENGTH);
|
|
35297
|
+
if (name.length === 0) continue;
|
|
35298
|
+
const size = entry.size_bytes;
|
|
35299
|
+
artifacts.push(
|
|
35300
|
+
typeof size === "number" && Number.isSafeInteger(size) && size >= 0 ? { name, sizeBytes: size } : { name }
|
|
35301
|
+
);
|
|
35302
|
+
}
|
|
35303
|
+
return artifacts;
|
|
35304
|
+
}
|
|
35305
|
+
function decodeMessageInstant(value) {
|
|
35306
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 && value < EPOCH_SECONDS_LIMIT ? new Date(Math.round(value) * 1e3).toISOString() : null;
|
|
35307
|
+
}
|
|
35032
35308
|
function decodeTurns(data, limit2) {
|
|
35033
|
-
if (!
|
|
35309
|
+
if (!isRecord4(data) || !Array.isArray(data.messages)) {
|
|
35034
35310
|
throw new PredispatchFailure("not-connected");
|
|
35035
35311
|
}
|
|
35036
35312
|
const turns = [];
|
|
35037
35313
|
for (const entry of data.messages) {
|
|
35038
|
-
if (!
|
|
35314
|
+
if (!isRecord4(entry)) continue;
|
|
35039
35315
|
if (entry.role !== "user" && entry.role !== "assistant") continue;
|
|
35040
35316
|
if (typeof entry.id !== "string" || !OPAQUE_ID.test(entry.id)) continue;
|
|
35041
35317
|
if (typeof entry.content !== "string") continue;
|
|
@@ -35044,12 +35320,52 @@ function decodeTurns(data, limit2) {
|
|
|
35044
35320
|
COMPANION_TURN_TEXT_MAX_CODE_POINTS
|
|
35045
35321
|
);
|
|
35046
35322
|
if (text.length === 0) continue;
|
|
35047
|
-
const at = typeof entry.createdAt === "number" && Number.isFinite(entry.createdAt) && entry.createdAt > 0 && entry.createdAt < EPOCH_SECONDS_LIMIT ? new Date(Math.round(entry.createdAt) * 1e3).toISOString() : null;
|
|
35048
35323
|
turns.push({
|
|
35049
35324
|
id: entry.id,
|
|
35050
35325
|
role: entry.role === "user" ? "user" : "mate",
|
|
35051
35326
|
text,
|
|
35052
|
-
at
|
|
35327
|
+
at: decodeMessageInstant(entry.createdAt)
|
|
35328
|
+
});
|
|
35329
|
+
}
|
|
35330
|
+
return turns.slice(-limit2);
|
|
35331
|
+
}
|
|
35332
|
+
function decodeDurableDecision(entry) {
|
|
35333
|
+
const frame = parseUiDirectiveFrame({
|
|
35334
|
+
directive: entry.uiDirective,
|
|
35335
|
+
source: entry.uiDirectiveSource === "voice" ? "voice" : "text"
|
|
35336
|
+
});
|
|
35337
|
+
if (!frame) return null;
|
|
35338
|
+
return toCompanionDecision(frame.directive);
|
|
35339
|
+
}
|
|
35340
|
+
function decodeRichTurns(data, limit2) {
|
|
35341
|
+
if (!isRecord4(data) || !Array.isArray(data.messages)) {
|
|
35342
|
+
throw new PredispatchFailure("not-connected");
|
|
35343
|
+
}
|
|
35344
|
+
const turns = [];
|
|
35345
|
+
for (const entry of data.messages) {
|
|
35346
|
+
if (!isRecord4(entry)) continue;
|
|
35347
|
+
if (entry.role !== "user" && entry.role !== "assistant") continue;
|
|
35348
|
+
if (typeof entry.id !== "string" || !OPAQUE_ID.test(entry.id)) continue;
|
|
35349
|
+
if (typeof entry.content !== "string") continue;
|
|
35350
|
+
const parsed = parseArtifacts(entry.content);
|
|
35351
|
+
const artifacts = toCompanionArtifacts(parsed.artifacts);
|
|
35352
|
+
const text = boundCodePoints(
|
|
35353
|
+
sanitizeMessageText(parsed.text),
|
|
35354
|
+
COMPANION_TURN_TEXT_MAX_CODE_POINTS
|
|
35355
|
+
);
|
|
35356
|
+
const at = decodeMessageInstant(entry.createdAt);
|
|
35357
|
+
const decision = entry.uiDirective === void 0 ? null : decodeDurableDecision(entry);
|
|
35358
|
+
if (decision !== null) {
|
|
35359
|
+
turns.push({ id: entry.id, role: "mate", text, at, decision });
|
|
35360
|
+
continue;
|
|
35361
|
+
}
|
|
35362
|
+
if (text.length === 0 && artifacts.length === 0) continue;
|
|
35363
|
+
turns.push({
|
|
35364
|
+
id: entry.id,
|
|
35365
|
+
role: entry.role === "user" ? "user" : "mate",
|
|
35366
|
+
text,
|
|
35367
|
+
at,
|
|
35368
|
+
...artifacts.length > 0 ? { artifacts } : {}
|
|
35053
35369
|
});
|
|
35054
35370
|
}
|
|
35055
35371
|
return turns.slice(-limit2);
|
|
@@ -35163,7 +35479,7 @@ async function drainAcceptedSse(response) {
|
|
|
35163
35479
|
continue;
|
|
35164
35480
|
}
|
|
35165
35481
|
const parsed = JSON.parse(data);
|
|
35166
|
-
if (!
|
|
35482
|
+
if (!isRecord4(parsed) || typeof parsed.type !== "string" || parsed.type === "error") {
|
|
35167
35483
|
throw new Error("invalid stream event");
|
|
35168
35484
|
}
|
|
35169
35485
|
}
|
|
@@ -35185,7 +35501,7 @@ function emitBoundedText(text, emit) {
|
|
|
35185
35501
|
}
|
|
35186
35502
|
if (slice.length > 0) emit(slice);
|
|
35187
35503
|
}
|
|
35188
|
-
async function streamAcceptedSse(response, now, onText) {
|
|
35504
|
+
async function streamAcceptedSse(response, now, onText, onData) {
|
|
35189
35505
|
if (!response.body) throw new Error("response body unavailable");
|
|
35190
35506
|
const reader = response.body.getReader();
|
|
35191
35507
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
@@ -35211,7 +35527,7 @@ async function streamAcceptedSse(response, now, onText) {
|
|
|
35211
35527
|
return;
|
|
35212
35528
|
}
|
|
35213
35529
|
const parsed = JSON.parse(data);
|
|
35214
|
-
if (!
|
|
35530
|
+
if (!isRecord4(parsed) || typeof parsed.type !== "string" || parsed.type === "error") {
|
|
35215
35531
|
throw new Error("invalid stream event");
|
|
35216
35532
|
}
|
|
35217
35533
|
if (parsed.type === "text-delta" && typeof parsed.delta === "string" && parsed.delta.length > 0) {
|
|
@@ -35220,6 +35536,10 @@ async function streamAcceptedSse(response, now, onText) {
|
|
|
35220
35536
|
flush();
|
|
35221
35537
|
}
|
|
35222
35538
|
}
|
|
35539
|
+
if (onData && parsed.type.startsWith("data-")) {
|
|
35540
|
+
flush();
|
|
35541
|
+
onData(parsed);
|
|
35542
|
+
}
|
|
35223
35543
|
};
|
|
35224
35544
|
const drainFrames = () => {
|
|
35225
35545
|
for (; ; ) {
|
|
@@ -35255,7 +35575,7 @@ async function streamAcceptedSse(response, now, onText) {
|
|
|
35255
35575
|
throw new Error("stream ended without terminal marker");
|
|
35256
35576
|
}
|
|
35257
35577
|
}
|
|
35258
|
-
async function dispatchConversation(request, sink, deps, consume, readDesktopMarker) {
|
|
35578
|
+
async function dispatchConversation(request, sink, deps, consume, readDesktopMarker, chatBody, classifyRefusal) {
|
|
35259
35579
|
const { controller, timer } = createDeadline(deps);
|
|
35260
35580
|
const fetchImpl = deps.fetch ?? fetch;
|
|
35261
35581
|
let route;
|
|
@@ -35301,17 +35621,19 @@ async function dispatchConversation(request, sink, deps, consume, readDesktopMar
|
|
|
35301
35621
|
chatResponse = await fetchImpl(`${origin}/api/chat`, {
|
|
35302
35622
|
method: "POST",
|
|
35303
35623
|
headers: { ...headers, "content-type": "application/json" },
|
|
35304
|
-
body: JSON.stringify(
|
|
35305
|
-
messages: [{ role: "user", content: request.text }],
|
|
35306
|
-
agentName,
|
|
35307
|
-
conversationId
|
|
35308
|
-
}),
|
|
35624
|
+
body: JSON.stringify(chatBody(agentName, conversationId)),
|
|
35309
35625
|
signal: controller.signal
|
|
35310
35626
|
});
|
|
35311
35627
|
} catch {
|
|
35312
35628
|
throw new Error("chat dispatch outcome unavailable");
|
|
35313
35629
|
}
|
|
35314
35630
|
if (!chatResponse.ok || !chatResponse.body || !(chatResponse.headers.get("content-type") ?? "").toLowerCase().includes("text/event-stream")) {
|
|
35631
|
+
if (!chatResponse.ok && classifyRefusal) {
|
|
35632
|
+
const reason = await classifyRefusal(chatResponse);
|
|
35633
|
+
if (reason !== null) {
|
|
35634
|
+
return emitFailure(sink, reason, request.requestId);
|
|
35635
|
+
}
|
|
35636
|
+
}
|
|
35315
35637
|
throw new Error("chat response not accepted");
|
|
35316
35638
|
}
|
|
35317
35639
|
const acceptedEvent = parseEvent({
|
|
@@ -35349,8 +35671,40 @@ async function dispatchConversation(request, sink, deps, consume, readDesktopMar
|
|
|
35349
35671
|
clearTimeout(timer);
|
|
35350
35672
|
}
|
|
35351
35673
|
}
|
|
35674
|
+
function textTurnBody(text) {
|
|
35675
|
+
return (agentName, conversationId) => ({
|
|
35676
|
+
messages: [{ role: "user", content: text }],
|
|
35677
|
+
agentName,
|
|
35678
|
+
conversationId
|
|
35679
|
+
});
|
|
35680
|
+
}
|
|
35681
|
+
function replyDataForwarder(requestId, sink) {
|
|
35682
|
+
return (part) => {
|
|
35683
|
+
if (part.type === UI_DIRECTIVE_PART_TYPE) {
|
|
35684
|
+
const frame = parseUiDirectiveFrame(part.data);
|
|
35685
|
+
if (!frame) return;
|
|
35686
|
+
const decision = toCompanionDecision(frame.directive);
|
|
35687
|
+
if (decision === null) return;
|
|
35688
|
+
sink(parseEvent({ type: "reply-decision", requestId, decision }));
|
|
35689
|
+
return;
|
|
35690
|
+
}
|
|
35691
|
+
if (part.type === ARTIFACTS_PART_TYPE && Array.isArray(part.data)) {
|
|
35692
|
+
const artifacts = toCompanionArtifacts(part.data);
|
|
35693
|
+
if (artifacts.length > 0) {
|
|
35694
|
+
sink(parseEvent({ type: "reply-artifacts", requestId, artifacts }));
|
|
35695
|
+
}
|
|
35696
|
+
}
|
|
35697
|
+
};
|
|
35698
|
+
}
|
|
35352
35699
|
async function sendCompanionMessage(request, sink, deps = {}) {
|
|
35353
|
-
return dispatchConversation(
|
|
35700
|
+
return dispatchConversation(
|
|
35701
|
+
request,
|
|
35702
|
+
sink,
|
|
35703
|
+
deps,
|
|
35704
|
+
drainAcceptedSse,
|
|
35705
|
+
true,
|
|
35706
|
+
textTurnBody(request.text)
|
|
35707
|
+
);
|
|
35354
35708
|
}
|
|
35355
35709
|
async function converseCompanionMessage(request, sink, deps = {}) {
|
|
35356
35710
|
return dispatchConversation(
|
|
@@ -35366,10 +35720,101 @@ async function converseCompanionMessage(request, sink, deps = {}) {
|
|
|
35366
35720
|
})
|
|
35367
35721
|
);
|
|
35368
35722
|
}),
|
|
35369
|
-
false
|
|
35723
|
+
false,
|
|
35724
|
+
textTurnBody(request.text)
|
|
35725
|
+
);
|
|
35726
|
+
}
|
|
35727
|
+
async function chatCompanionMessage(request, sink, deps = {}) {
|
|
35728
|
+
return dispatchConversation(
|
|
35729
|
+
request,
|
|
35730
|
+
sink,
|
|
35731
|
+
deps,
|
|
35732
|
+
(response) => streamAcceptedSse(
|
|
35733
|
+
response,
|
|
35734
|
+
deps.now ?? Date.now,
|
|
35735
|
+
(text) => {
|
|
35736
|
+
sink(
|
|
35737
|
+
parseEvent({
|
|
35738
|
+
type: "reply-delta",
|
|
35739
|
+
requestId: request.requestId,
|
|
35740
|
+
text
|
|
35741
|
+
})
|
|
35742
|
+
);
|
|
35743
|
+
},
|
|
35744
|
+
replyDataForwarder(request.requestId, sink)
|
|
35745
|
+
),
|
|
35746
|
+
false,
|
|
35747
|
+
textTurnBody(request.text)
|
|
35748
|
+
);
|
|
35749
|
+
}
|
|
35750
|
+
async function classifyDecideRefusal(response) {
|
|
35751
|
+
let reason;
|
|
35752
|
+
try {
|
|
35753
|
+
const envelope = JSON.parse(await readBoundedText(response));
|
|
35754
|
+
if (isRecord4(envelope) && isRecord4(envelope.error) && isRecord4(envelope.error.details)) {
|
|
35755
|
+
reason = envelope.error.details.reason;
|
|
35756
|
+
}
|
|
35757
|
+
} catch {
|
|
35758
|
+
}
|
|
35759
|
+
if (isDecisionRefusalReason(reason)) {
|
|
35760
|
+
return reason === DECISION_REFUSAL_REASONS.notPending ? "decision-not-pending" : "bad-request";
|
|
35761
|
+
}
|
|
35762
|
+
if (response.status === 401 || response.status === 403) {
|
|
35763
|
+
return "not-authorized";
|
|
35764
|
+
}
|
|
35765
|
+
if (response.status === 400) return "bad-request";
|
|
35766
|
+
if (response.status === 409) return "busy";
|
|
35767
|
+
return null;
|
|
35768
|
+
}
|
|
35769
|
+
async function decideCompanionMessage(request, sink, deps = {}) {
|
|
35770
|
+
const message = {
|
|
35771
|
+
role: "user",
|
|
35772
|
+
parts: [
|
|
35773
|
+
{
|
|
35774
|
+
type: UI_DIRECTIVE_RESULT_PART_TYPE,
|
|
35775
|
+
data: {
|
|
35776
|
+
version: 1,
|
|
35777
|
+
name: "present_decision",
|
|
35778
|
+
callId: request.callId,
|
|
35779
|
+
result: { status: "selected", optionIndex: request.optionIndex }
|
|
35780
|
+
}
|
|
35781
|
+
}
|
|
35782
|
+
]
|
|
35783
|
+
};
|
|
35784
|
+
return dispatchConversation(
|
|
35785
|
+
request,
|
|
35786
|
+
sink,
|
|
35787
|
+
deps,
|
|
35788
|
+
(response) => streamAcceptedSse(
|
|
35789
|
+
response,
|
|
35790
|
+
deps.now ?? Date.now,
|
|
35791
|
+
(text) => {
|
|
35792
|
+
sink(
|
|
35793
|
+
parseEvent({
|
|
35794
|
+
type: "reply-delta",
|
|
35795
|
+
requestId: request.requestId,
|
|
35796
|
+
text
|
|
35797
|
+
})
|
|
35798
|
+
);
|
|
35799
|
+
},
|
|
35800
|
+
replyDataForwarder(request.requestId, sink)
|
|
35801
|
+
),
|
|
35802
|
+
false,
|
|
35803
|
+
(agentName, conversationId) => ({
|
|
35804
|
+
messages: [message],
|
|
35805
|
+
agentName,
|
|
35806
|
+
conversationId
|
|
35807
|
+
}),
|
|
35808
|
+
classifyDecideRefusal
|
|
35370
35809
|
);
|
|
35371
35810
|
}
|
|
35372
35811
|
async function historyCompanionMessages(request, sink, deps = {}) {
|
|
35812
|
+
return fetchConversationTurns(request, sink, deps, decodeTurns);
|
|
35813
|
+
}
|
|
35814
|
+
async function recallCompanionMessages(request, sink, deps = {}) {
|
|
35815
|
+
return fetchConversationTurns(request, sink, deps, decodeRichTurns);
|
|
35816
|
+
}
|
|
35817
|
+
async function fetchConversationTurns(request, sink, deps, decode) {
|
|
35373
35818
|
const { controller, timer } = createDeadline(deps);
|
|
35374
35819
|
const fetchImpl = deps.fetch ?? fetch;
|
|
35375
35820
|
try {
|
|
@@ -35389,7 +35834,7 @@ async function historyCompanionMessages(request, sink, deps = {}) {
|
|
|
35389
35834
|
agentName,
|
|
35390
35835
|
controller.signal
|
|
35391
35836
|
);
|
|
35392
|
-
const turns =
|
|
35837
|
+
const turns = decode(
|
|
35393
35838
|
await fetchConversationMessages(
|
|
35394
35839
|
fetchImpl,
|
|
35395
35840
|
origin,
|
|
@@ -35545,7 +35990,10 @@ async function dispatchRequest(request, sink, handlers) {
|
|
|
35545
35990
|
return terminal;
|
|
35546
35991
|
}
|
|
35547
35992
|
if (request.type === "history") return handlers.history(request, sink);
|
|
35993
|
+
if (request.type === "recall") return handlers.recall(request, sink);
|
|
35548
35994
|
if (request.type === "converse") return handlers.converse(request, sink);
|
|
35995
|
+
if (request.type === "chat") return handlers.chat(request, sink);
|
|
35996
|
+
if (request.type === "decide") return handlers.decide(request, sink);
|
|
35549
35997
|
return handlers.send(request, sink);
|
|
35550
35998
|
}
|
|
35551
35999
|
async function handleWithContext(request, sink, handlers, context) {
|
|
@@ -35581,7 +36029,10 @@ async function runCompanionBridgeServe(stdin, stdout, stderr, deps = {}) {
|
|
|
35581
36029
|
roster: (sink2) => rosterCompanions(sink2, chat),
|
|
35582
36030
|
send: (request, sink2) => sendCompanionMessage(request, sink2, chat),
|
|
35583
36031
|
converse: (request, sink2) => converseCompanionMessage(request, sink2, chat),
|
|
36032
|
+
chat: (request, sink2) => chatCompanionMessage(request, sink2, chat),
|
|
36033
|
+
decide: (request, sink2) => decideCompanionMessage(request, sink2, chat),
|
|
35584
36034
|
history: (request, sink2) => historyCompanionMessages(request, sink2, chat),
|
|
36035
|
+
recall: (request, sink2) => recallCompanionMessages(request, sink2, chat),
|
|
35585
36036
|
update: deps.update ?? (() => checkCompanionUpdate(defaultLocalCompanionInstallOptions()))
|
|
35586
36037
|
};
|
|
35587
36038
|
const sink = (event) => {
|
|
@@ -35616,7 +36067,10 @@ var defaultDeps4 = {
|
|
|
35616
36067
|
roster: rosterCompanions,
|
|
35617
36068
|
send: sendCompanionMessage,
|
|
35618
36069
|
converse: converseCompanionMessage,
|
|
36070
|
+
chat: chatCompanionMessage,
|
|
36071
|
+
decide: decideCompanionMessage,
|
|
35619
36072
|
history: historyCompanionMessages,
|
|
36073
|
+
recall: recallCompanionMessages,
|
|
35620
36074
|
update: () => checkCompanionUpdate(defaultLocalCompanionInstallOptions())
|
|
35621
36075
|
};
|
|
35622
36076
|
async function readSingleRequest(stdin) {
|
|
@@ -35667,8 +36121,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps4) {
|
|
|
35667
36121
|
sink(terminal);
|
|
35668
36122
|
} else if (request.type === "history") {
|
|
35669
36123
|
terminal = await deps.history(request, sink);
|
|
36124
|
+
} else if (request.type === "recall") {
|
|
36125
|
+
terminal = await deps.recall(request, sink);
|
|
35670
36126
|
} else if (request.type === "converse") {
|
|
35671
36127
|
terminal = await deps.converse(request, sink);
|
|
36128
|
+
} else if (request.type === "chat") {
|
|
36129
|
+
terminal = await deps.chat(request, sink);
|
|
36130
|
+
} else if (request.type === "decide") {
|
|
36131
|
+
terminal = await deps.decide(request, sink);
|
|
35672
36132
|
} else terminal = await deps.send(request, sink);
|
|
35673
36133
|
return exitFor(terminal);
|
|
35674
36134
|
} catch {
|