@on-mission/sdk 0.1.12 → 0.1.13
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/index.d.ts +65 -2
- package/dist/index.js +250 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FileRef, StagingProvenance, ToolCallInput } from '@mission/models';
|
|
2
|
-
export { FileRef, FileRefStore, MAX_STAGING_UPLOAD_BYTES, StagingProvenance, Tool, ToolCallContext, ToolCallEnvelope, ToolCallInput, ToolFunction, ToolFunctionPermission, ToolKind, fileRefSchema } from '@mission/models';
|
|
2
|
+
export { AuthoredToolResultTransport, FileRef, FileRefStore, IdentityBearingKey, MAX_STAGING_UPLOAD_BYTES, StagingProvenance, TOOL_RESULT_TRANSPORT_MAX_BYTES, Tool, ToolCallContext, ToolCallEnvelope, ToolCallInput, ToolFunction, ToolFunctionPermission, ToolKind, ToolResultEnvelope, ToolResultTransport, fileRefSchema, toolResult } from '@mission/models';
|
|
3
3
|
import { createTRPCProxyClient } from '@trpc/client';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -153,6 +153,69 @@ type ConfigModule = {
|
|
|
153
153
|
get: <T = Record<string, unknown>>(defaults?: Partial<T>) => Promise<T>;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Content-identity test kit (MIS-649).
|
|
158
|
+
*
|
|
159
|
+
* Mission content-addresses tool results: the platform serializes a result,
|
|
160
|
+
* sha256s the bytes, and that digest becomes the sandbox filename, the GCS key,
|
|
161
|
+
* and the dedup key. A tool that echoes a volatile value INSIDE its result
|
|
162
|
+
* payload — a pagination cursor, a request id, a fetch timestamp — makes two
|
|
163
|
+
* byte-identical result SETS hash differently. Dedup silently fails and the
|
|
164
|
+
* model is handed N artifacts where it should have seen one.
|
|
165
|
+
*
|
|
166
|
+
* `toolResult(content, transport)` is the fix: the platform hashes `content`
|
|
167
|
+
* alone and excludes `transport`. But a tool only gets that property if it puts
|
|
168
|
+
* the volatile fields in the right half, and NOTHING about the return type can
|
|
169
|
+
* tell it apart from getting it wrong — both compile.
|
|
170
|
+
*
|
|
171
|
+
* `assertContentIdentityStable` is the check that can. It runs a tool's real
|
|
172
|
+
* invocations and asserts the two properties that matter:
|
|
173
|
+
*
|
|
174
|
+
* 1. Two calls that returned the SAME records with a DIFFERENT volatile value
|
|
175
|
+
* produce the SAME content identity.
|
|
176
|
+
* 2. A call that returned DIFFERENT records produces a DIFFERENT one.
|
|
177
|
+
*
|
|
178
|
+
* Property 2 is not decoration — without it, a tool that dumps its whole payload
|
|
179
|
+
* into `transport` (or returns a constant) passes property 1 perfectly and has
|
|
180
|
+
* destroyed the very identity the store depends on.
|
|
181
|
+
*
|
|
182
|
+
* It is framework-agnostic on purpose: it throws a plain `Error` on failure, so
|
|
183
|
+
* it works under vitest, node:test, or a plain script, and importing it never
|
|
184
|
+
* drags a test runner into a skill's runtime bundle.
|
|
185
|
+
*/
|
|
186
|
+
/**
|
|
187
|
+
* The content identity the PLATFORM would compute for a tool result: sha256 over
|
|
188
|
+
* the serialized bytes of the hashed subject that `selectHashedContent` picks.
|
|
189
|
+
*
|
|
190
|
+
* BOTH halves are imported, not reimplemented. The selection
|
|
191
|
+
* (`selectHashedContent`) and the serialization (`serializeForIdentity`) are the
|
|
192
|
+
* platform's own definitions, so this function cannot report an address the
|
|
193
|
+
* platform will not produce. A local re-implementation of either half is exactly
|
|
194
|
+
* the defect this contract exists to eliminate, one level up.
|
|
195
|
+
*/
|
|
196
|
+
declare const contentIdentity: (result: unknown) => string;
|
|
197
|
+
declare const assertContentIdentityContractLoaded: () => void;
|
|
198
|
+
type ContentIdentityStableInput = Readonly<{
|
|
199
|
+
/**
|
|
200
|
+
* Two invocations that returned THE SAME RECORDS but a different volatile
|
|
201
|
+
* value (page 1 fetched twice with a fresh cursor, the same query re-run with a
|
|
202
|
+
* new request id). Their content identity must match.
|
|
203
|
+
*/
|
|
204
|
+
sameRecordsDifferentCursor: readonly [
|
|
205
|
+
() => Promise<unknown> | unknown,
|
|
206
|
+
() => Promise<unknown> | unknown
|
|
207
|
+
];
|
|
208
|
+
/**
|
|
209
|
+
* An invocation that returned DIFFERENT records. Its content identity must
|
|
210
|
+
* differ from the pair above — the guard against a tool that has collapsed all
|
|
211
|
+
* of its results onto one address.
|
|
212
|
+
*/
|
|
213
|
+
differentRecords: () => Promise<unknown> | unknown;
|
|
214
|
+
/** Optional label used in failure messages (e.g. the tool name). */
|
|
215
|
+
label?: string;
|
|
216
|
+
}>;
|
|
217
|
+
declare const assertContentIdentityStable: (input: ContentIdentityStableInput) => Promise<void>;
|
|
218
|
+
|
|
156
219
|
/**
|
|
157
220
|
* Debug Logging — Local Development
|
|
158
221
|
*
|
|
@@ -888,4 +951,4 @@ type WebhookResult = {
|
|
|
888
951
|
response?: WebhookResponse;
|
|
889
952
|
};
|
|
890
953
|
|
|
891
|
-
export { type AgentMessage, type ApiClient, type AttachmentLimits, type AuthCredentials, type AuthModule, type ConfigModule, EmailAttachments, type EventHandler, type EventHandlerRegistry, FileTransferTimeoutError, type FilesModule, type IngressHandler, type IngressModule, type IngressRequest, type IngressResponse, type IngressStreamEvents, type IngressUrl, type LogModule, type MimeBodyParts, type MissionSDK, type MissionSDKOptions, type OAuthCredentials, type RedeemedAttachment, type SendIntentClaimOutcome, type SendIntentCompletion, type SendIntentRecord, type SendIntentStatus, type SendIntentsModule, type SendMessageId, type SendMessageIdInput, type SkillContext, type SkillEvent, SkillToolHttpError, type ToolHandler, type ToolsModule, type WebhookRequest, type WebhookResponse, type WebhookResult, clearDebugLog, createMissionSDK, debugLog };
|
|
954
|
+
export { type AgentMessage, type ApiClient, type AttachmentLimits, type AuthCredentials, type AuthModule, type ConfigModule, type ContentIdentityStableInput, EmailAttachments, type EventHandler, type EventHandlerRegistry, FileTransferTimeoutError, type FilesModule, type IngressHandler, type IngressModule, type IngressRequest, type IngressResponse, type IngressStreamEvents, type IngressUrl, type LogModule, type MimeBodyParts, type MissionSDK, type MissionSDKOptions, type OAuthCredentials, type RedeemedAttachment, type SendIntentClaimOutcome, type SendIntentCompletion, type SendIntentRecord, type SendIntentStatus, type SendIntentsModule, type SendMessageId, type SendMessageIdInput, type SkillContext, type SkillEvent, SkillToolHttpError, type ToolHandler, type ToolsModule, type WebhookRequest, type WebhookResponse, type WebhookResult, assertContentIdentityContractLoaded, assertContentIdentityStable, clearDebugLog, contentIdentity, createMissionSDK, debugLog };
|
package/dist/index.js
CHANGED
|
@@ -14256,7 +14256,6 @@ var hostFsOpSchema = external_exports.union([
|
|
|
14256
14256
|
external_exports.literal("move"),
|
|
14257
14257
|
external_exports.literal("delete")
|
|
14258
14258
|
]);
|
|
14259
|
-
var wireGrantsSchema = external_exports.array(external_exports.unknown());
|
|
14260
14259
|
var workingFileSchema = external_exports.strictObject({
|
|
14261
14260
|
path: external_exports.string().min(1),
|
|
14262
14261
|
name: external_exports.string().min(1),
|
|
@@ -14269,7 +14268,6 @@ var invokeFrameSchema = external_exports.object({
|
|
|
14269
14268
|
particleId: particleIdSchema,
|
|
14270
14269
|
action: external_exports.string().min(1),
|
|
14271
14270
|
input: external_exports.unknown(),
|
|
14272
|
-
grants: wireGrantsSchema,
|
|
14273
14271
|
workingFile: workingFileSchema.optional()
|
|
14274
14272
|
});
|
|
14275
14273
|
var resultOutcomeSchema = external_exports.union([
|
|
@@ -14344,16 +14342,10 @@ var childToSupervisorFrameSchema = external_exports.discriminatedUnion("type", [
|
|
|
14344
14342
|
heartbeatFrameSchema
|
|
14345
14343
|
]);
|
|
14346
14344
|
|
|
14345
|
+
// ../models/src/skill-runtime/fs-root.ts
|
|
14346
|
+
var rootSchema = external_exports.enum(["documents", "downloads", "desktop"]);
|
|
14347
|
+
|
|
14347
14348
|
// ../models/src/skill-runtime/manifest-verbs.ts
|
|
14348
|
-
var capabilityKindSchema = external_exports.union([
|
|
14349
|
-
external_exports.literal("fs:read:scoped"),
|
|
14350
|
-
external_exports.literal("fs:write:scoped"),
|
|
14351
|
-
external_exports.literal("fs:read:host"),
|
|
14352
|
-
external_exports.literal("fs:write:host"),
|
|
14353
|
-
external_exports.literal("proc:read"),
|
|
14354
|
-
external_exports.literal("net:egress"),
|
|
14355
|
-
external_exports.literal("exec:subprocess")
|
|
14356
|
-
]);
|
|
14357
14349
|
var manifestVerbPermissionSchema = external_exports.union([
|
|
14358
14350
|
external_exports.literal("enabled"),
|
|
14359
14351
|
external_exports.literal("disabled"),
|
|
@@ -14378,22 +14370,14 @@ var manifestVerbSchema = external_exports.object({
|
|
|
14378
14370
|
action: external_exports.string().min(1),
|
|
14379
14371
|
inputSchema: jsonObjectSchema,
|
|
14380
14372
|
prompt: manifestVerbPromptSchema,
|
|
14381
|
-
capability: capabilityKindSchema,
|
|
14382
14373
|
permissions: manifestVerbPermissionFlagsSchema
|
|
14383
|
-
});
|
|
14374
|
+
}).strict();
|
|
14384
14375
|
var manifestDeviceBlockSchema = external_exports.object({
|
|
14385
14376
|
eligibility: external_exports.object({
|
|
14386
14377
|
os: external_exports.array(external_exports.string()),
|
|
14387
14378
|
arch: external_exports.array(external_exports.string())
|
|
14388
|
-
})
|
|
14389
|
-
|
|
14390
|
-
external_exports.union([
|
|
14391
|
-
external_exports.literal("documents"),
|
|
14392
|
-
external_exports.literal("downloads"),
|
|
14393
|
-
external_exports.literal("desktop")
|
|
14394
|
-
])
|
|
14395
|
-
)
|
|
14396
|
-
});
|
|
14379
|
+
})
|
|
14380
|
+
}).strict();
|
|
14397
14381
|
var manifestSandboxBlockSchema = external_exports.object({
|
|
14398
14382
|
start: external_exports.string().min(1)
|
|
14399
14383
|
});
|
|
@@ -14420,6 +14404,162 @@ var MAX = Number.MAX_SAFE_INTEGER;
|
|
|
14420
14404
|
var MAX_BIGINT = BigInt(MAX);
|
|
14421
14405
|
|
|
14422
14406
|
// ../models/src/tools.ts
|
|
14407
|
+
var TOOL_RESULT_TRANSPORT_MAX_BYTES = 4096;
|
|
14408
|
+
var utf8ByteLength = (value) => new TextEncoder().encode(value).length;
|
|
14409
|
+
var toolResultTransportSchema = external_exports.record(
|
|
14410
|
+
external_exports.string(),
|
|
14411
|
+
// `.optional()` (MIS-649 follow-up): an EXPLICITLY-undefined value is
|
|
14412
|
+
// accepted and stripped, so `{ cursor: maybeUndefined }` — the natural
|
|
14413
|
+
// spelling when a cursor may be absent — behaves exactly like an absent
|
|
14414
|
+
// key. Rejecting it made the whole result `malformed`, which on the inline
|
|
14415
|
+
// path handed the model the raw `{"kind":"tool-result-envelope",…}` wrapper
|
|
14416
|
+
// and lost dedup: a punishing outcome for the obvious spelling.
|
|
14417
|
+
external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]).optional()
|
|
14418
|
+
).refine(
|
|
14419
|
+
(value) => utf8ByteLength(JSON.stringify(value)) <= TOOL_RESULT_TRANSPORT_MAX_BYTES,
|
|
14420
|
+
{
|
|
14421
|
+
message: `transport exceeds ${TOOL_RESULT_TRANSPORT_MAX_BYTES} bytes when JSON-serialized`
|
|
14422
|
+
}
|
|
14423
|
+
);
|
|
14424
|
+
var toolResultEnvelopeTagSchema = external_exports.object({
|
|
14425
|
+
kind: external_exports.literal("tool-result-envelope")
|
|
14426
|
+
});
|
|
14427
|
+
var toolResultEnvelopeSchema = external_exports.object({
|
|
14428
|
+
kind: external_exports.literal("tool-result-envelope"),
|
|
14429
|
+
/**
|
|
14430
|
+
* `z.unknown()` is INTENTIONAL here and is not an R2 violation waiting to be
|
|
14431
|
+
* fixed. R2 bans `z.unknown()` in a schema that defines a contract with the
|
|
14432
|
+
* outside world — because the field then reaches downstream code untyped.
|
|
14433
|
+
* This contract deliberately ENDS at the envelope: the platform's whole job
|
|
14434
|
+
* is to serialize and hash `content` without knowing its shape, and the
|
|
14435
|
+
* moment the platform can describe that shape it has acquired knowledge of a
|
|
14436
|
+
* particular tool. There is no downstream code that reads a field off
|
|
14437
|
+
* `content`; modeling "the real shape" here is not merely unnecessary, it is
|
|
14438
|
+
* the exact coupling this type exists to prevent. Presence (not shape) is
|
|
14439
|
+
* what the platform requires, and the refinement below enforces it.
|
|
14440
|
+
*/
|
|
14441
|
+
content: external_exports.unknown(),
|
|
14442
|
+
/** transport holds only values that would differ between two calls that returned
|
|
14443
|
+
* the same result — echoed cursors, request ids, fetch timestamps. If two results
|
|
14444
|
+
* differing only in this field are genuinely *different results*, it belongs in
|
|
14445
|
+
* `content` (`complete`, `capReached`, `truncated`, `total`, `exitCode` all belong
|
|
14446
|
+
* in `content`). Governs identity, not representation.
|
|
14447
|
+
*
|
|
14448
|
+
* TAINT-BEARING FIELDS BELONG IN `content`, NEVER IN `transport`. Downstream
|
|
14449
|
+
* taint admission parses `taintSources` off the tool result AFTER
|
|
14450
|
+
* `wrapToolResult` has run (see
|
|
14451
|
+
* `apps/temporal/src/workflows/agent-conversation/activities/conversation-history-taint.ts`),
|
|
14452
|
+
* and by then `transport` has been split off the hashed bytes and rides on the
|
|
14453
|
+
* reference instead. A tool that moved `taintSources` into `transport` would
|
|
14454
|
+
* lose its taint SILENTLY — no schema error, no log, just an untainted turn.
|
|
14455
|
+
* `transport` is for values the platform is contractually indifferent to;
|
|
14456
|
+
* taint is the opposite of indifferent. */
|
|
14457
|
+
transport: toolResultTransportSchema.optional()
|
|
14458
|
+
}).strict().refine((value) => value.content !== void 0, {
|
|
14459
|
+
message: "tool-result envelope `content` is missing or undefined"
|
|
14460
|
+
});
|
|
14461
|
+
var toolResult = (content, transport) => transport === void 0 ? { kind: "tool-result-envelope", content } : { kind: "tool-result-envelope", content, transport };
|
|
14462
|
+
var definedTransport = (transport) => {
|
|
14463
|
+
if (transport === void 0) return void 0;
|
|
14464
|
+
const entries = Object.entries(transport).filter(
|
|
14465
|
+
([, value]) => value !== void 0
|
|
14466
|
+
);
|
|
14467
|
+
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
14468
|
+
};
|
|
14469
|
+
var selectHashedContent = (input) => {
|
|
14470
|
+
const probe = () => {
|
|
14471
|
+
const claimsEnvelope = toolResultEnvelopeTagSchema.safeParse(input.result);
|
|
14472
|
+
if (!claimsEnvelope.success) {
|
|
14473
|
+
return {
|
|
14474
|
+
classification: "absent",
|
|
14475
|
+
hashed: input.offloadText ?? input.result
|
|
14476
|
+
};
|
|
14477
|
+
}
|
|
14478
|
+
const parsed = toolResultEnvelopeSchema.safeParse(input.result);
|
|
14479
|
+
if (!parsed.success) {
|
|
14480
|
+
return {
|
|
14481
|
+
classification: "malformed",
|
|
14482
|
+
hashed: input.offloadText ?? input.result,
|
|
14483
|
+
reason: parsed.error.issues.map((issue2) => `${issue2.path.join(".") || "<root>"}: ${issue2.message}`).join("; ")
|
|
14484
|
+
};
|
|
14485
|
+
}
|
|
14486
|
+
const transport = definedTransport(parsed.data.transport);
|
|
14487
|
+
return {
|
|
14488
|
+
classification: "valid",
|
|
14489
|
+
hashed: input.offloadText ?? parsed.data.content,
|
|
14490
|
+
transport
|
|
14491
|
+
};
|
|
14492
|
+
};
|
|
14493
|
+
try {
|
|
14494
|
+
return probe();
|
|
14495
|
+
} catch (err) {
|
|
14496
|
+
return {
|
|
14497
|
+
classification: "malformed",
|
|
14498
|
+
hashed: input.offloadText ?? input.result,
|
|
14499
|
+
reason: `envelope probe threw while reading the result: ${err instanceof Error ? err.message : String(err)}`
|
|
14500
|
+
};
|
|
14501
|
+
}
|
|
14502
|
+
};
|
|
14503
|
+
var serializeForIdentity = (value) => {
|
|
14504
|
+
if (value instanceof Uint8Array) {
|
|
14505
|
+
return {
|
|
14506
|
+
bytes: value,
|
|
14507
|
+
contentType: "application/octet-stream",
|
|
14508
|
+
ext: "bin",
|
|
14509
|
+
previewSource: `[binary ${value.length}b]`
|
|
14510
|
+
};
|
|
14511
|
+
}
|
|
14512
|
+
if (typeof value === "string") {
|
|
14513
|
+
const isJsonString = (s) => {
|
|
14514
|
+
try {
|
|
14515
|
+
JSON.parse(s);
|
|
14516
|
+
return true;
|
|
14517
|
+
} catch {
|
|
14518
|
+
return false;
|
|
14519
|
+
}
|
|
14520
|
+
};
|
|
14521
|
+
const isJson = isJsonString(value);
|
|
14522
|
+
return {
|
|
14523
|
+
bytes: new TextEncoder().encode(value),
|
|
14524
|
+
contentType: isJson ? "application/json" : "text/plain",
|
|
14525
|
+
ext: isJson ? "json" : "txt",
|
|
14526
|
+
previewSource: value
|
|
14527
|
+
};
|
|
14528
|
+
}
|
|
14529
|
+
const safeJson = (v) => {
|
|
14530
|
+
try {
|
|
14531
|
+
return JSON.stringify(
|
|
14532
|
+
v ?? null,
|
|
14533
|
+
(_, x) => typeof x === "bigint" ? x.toString() : x
|
|
14534
|
+
);
|
|
14535
|
+
} catch {
|
|
14536
|
+
return null;
|
|
14537
|
+
}
|
|
14538
|
+
};
|
|
14539
|
+
const json2 = safeJson(value);
|
|
14540
|
+
if (typeof json2 === "string") {
|
|
14541
|
+
return {
|
|
14542
|
+
bytes: new TextEncoder().encode(json2),
|
|
14543
|
+
contentType: "application/json",
|
|
14544
|
+
ext: "json",
|
|
14545
|
+
previewSource: json2
|
|
14546
|
+
};
|
|
14547
|
+
}
|
|
14548
|
+
const safeText = (v) => {
|
|
14549
|
+
try {
|
|
14550
|
+
return String(v);
|
|
14551
|
+
} catch {
|
|
14552
|
+
return `[unserializable ${typeof v}]`;
|
|
14553
|
+
}
|
|
14554
|
+
};
|
|
14555
|
+
const text = safeText(value);
|
|
14556
|
+
return {
|
|
14557
|
+
bytes: new TextEncoder().encode(text),
|
|
14558
|
+
contentType: "text/plain",
|
|
14559
|
+
ext: "txt",
|
|
14560
|
+
previewSource: text
|
|
14561
|
+
};
|
|
14562
|
+
};
|
|
14423
14563
|
var DEVICE_WRITE_TOOL_NAME_LIST = [
|
|
14424
14564
|
"file_write",
|
|
14425
14565
|
"file_update",
|
|
@@ -14441,7 +14581,8 @@ var DEVICE_DELIVERABLE_TOOL_NAME_LIST = [
|
|
|
14441
14581
|
var DEVICE_READ_TOOL_NAME_LIST = [
|
|
14442
14582
|
"file_read",
|
|
14443
14583
|
"file_list",
|
|
14444
|
-
|
|
14584
|
+
// search_files — ripgrep-powered find-and-count; a read/extract verb.
|
|
14585
|
+
"search_files",
|
|
14445
14586
|
"excel_read",
|
|
14446
14587
|
"word_read",
|
|
14447
14588
|
"pdf_read",
|
|
@@ -14489,8 +14630,83 @@ var PLATFORM_READ_OR_GATHER_TOOL_NAMES = new Set(
|
|
|
14489
14630
|
PLATFORM_READ_OR_GATHER_TOOL_NAME_LIST
|
|
14490
14631
|
);
|
|
14491
14632
|
|
|
14633
|
+
// src/content-identity.ts
|
|
14634
|
+
import { createHash } from "crypto";
|
|
14635
|
+
var contentIdentity = (result) => {
|
|
14636
|
+
assertNotWireWrapped(result, "contentIdentity");
|
|
14637
|
+
const { hashed } = selectHashedContent({ result });
|
|
14638
|
+
return createHash("sha256").update(serializeForIdentity(hashed).bytes).digest("hex");
|
|
14639
|
+
};
|
|
14640
|
+
var PROBE_CONTENT = { records: [{ id: "probe-1" }] };
|
|
14641
|
+
var PROBE_OTHER_CONTENT = { records: [{ id: "probe-2" }] };
|
|
14642
|
+
var staleBuildAdvice = "The loaded @on-mission/sdk build does not implement the content-identity envelope contract (MIS-649). This is almost certainly a STALE BUILD: `@mission/models` is source-only and is bundled into `packages/sdk/dist`, and the turbo `test` task does not depend on `build`, so editing `packages/models/src/tools.ts` leaves the dist that tests actually import untouched. Rebuild it \u2014 `yarn turbo build --filter=@on-mission/sdk` \u2014 and run again. If a fresh build still fails this check, the contract itself is broken and every content-identity assertion below it is meaningless.";
|
|
14643
|
+
var WIRE_WRAPPER_KEY = "result";
|
|
14644
|
+
var looksLikeUndecodedWireResponse = (value) => {
|
|
14645
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
14646
|
+
return false;
|
|
14647
|
+
}
|
|
14648
|
+
const keys = Object.keys(value);
|
|
14649
|
+
return keys.length === 1 && keys[0] === WIRE_WRAPPER_KEY;
|
|
14650
|
+
};
|
|
14651
|
+
var assertNotWireWrapped = (value, where) => {
|
|
14652
|
+
if (!looksLikeUndecodedWireResponse(value)) return;
|
|
14653
|
+
throw new Error(
|
|
14654
|
+
`${where}: the value handed to the content-identity module has exactly one key, \`result\` \u2014 which is the shape \`@on-mission/sdk\` puts on the WIRE (\`POST /_mission/tools/:toolName\` answers \`{ result: <handler return> }\`), not the shape a handler returns. The likely cause is an UN-DECODED SDK wire response: something passed \`await response.json()\` straight into identity instead of the platform's decoded result (\`parseSkillToolResponse\`). Hashing the wrapper is the MIS-649 production defect \u2014 the envelope tag probe tests the wrapper, classification is \`absent\`, and a volatile cursor stays in the hashed bytes. Pass the handler's own return value. (If your handler genuinely returns \`{ result: \u2026 }\` and nothing else, that shape is indistinguishable from the wrapper \u2014 give the payload a name.)`
|
|
14655
|
+
);
|
|
14656
|
+
};
|
|
14657
|
+
var assertContentIdentityContractLoaded = () => {
|
|
14658
|
+
const cursorA = contentIdentity(toolResult(PROBE_CONTENT, { offset: "A" }));
|
|
14659
|
+
const cursorB = contentIdentity(
|
|
14660
|
+
toolResult(PROBE_CONTENT, { offset: "B-TOTALLY-DIFFERENT" })
|
|
14661
|
+
);
|
|
14662
|
+
if (cursorA !== cursorB) {
|
|
14663
|
+
throw new Error(
|
|
14664
|
+
`content-identity contract NOT PRESENT in the loaded build: an identical \`content\` under two different \`transport\` values hashed differently (${cursorA} vs ${cursorB}); transport is supposed to be excluded from the digest entirely. ${staleBuildAdvice}`
|
|
14665
|
+
);
|
|
14666
|
+
}
|
|
14667
|
+
const other = contentIdentity(
|
|
14668
|
+
toolResult(PROBE_OTHER_CONTENT, { offset: "A" })
|
|
14669
|
+
);
|
|
14670
|
+
if (other === cursorA) {
|
|
14671
|
+
throw new Error(
|
|
14672
|
+
`content-identity contract NOT PRESENT in the loaded build: two DIFFERENT \`content\` values hashed to the same digest (${other}); the digest is not reading \`content\` at all. ${staleBuildAdvice}`
|
|
14673
|
+
);
|
|
14674
|
+
}
|
|
14675
|
+
const armed = (() => {
|
|
14676
|
+
try {
|
|
14677
|
+
contentIdentity({ result: PROBE_CONTENT });
|
|
14678
|
+
return false;
|
|
14679
|
+
} catch {
|
|
14680
|
+
return true;
|
|
14681
|
+
}
|
|
14682
|
+
})();
|
|
14683
|
+
if (!armed) {
|
|
14684
|
+
throw new Error(
|
|
14685
|
+
`content-identity contract NOT PRESENT in the loaded build: the wire-wrapper tripwire did not fire for \`{ result: \u2026 }\`, so an un-decoded SDK wire response would be hashed as if it were a tool result \u2014 the MIS-649 defect, undetected. ${staleBuildAdvice}`
|
|
14686
|
+
);
|
|
14687
|
+
}
|
|
14688
|
+
};
|
|
14689
|
+
var assertContentIdentityStable = async (input) => {
|
|
14690
|
+
assertContentIdentityContractLoaded();
|
|
14691
|
+
const where = input.label === void 0 ? "" : ` [${input.label}]`;
|
|
14692
|
+
const [firstCall, secondCall] = input.sameRecordsDifferentCursor;
|
|
14693
|
+
const first = contentIdentity(await firstCall());
|
|
14694
|
+
const second = contentIdentity(await secondCall());
|
|
14695
|
+
if (first !== second) {
|
|
14696
|
+
throw new Error(
|
|
14697
|
+
`content identity is NOT stable${where}: two calls returning the same records hashed differently (${first} vs ${second}). A volatile value \u2014 a cursor, a request id, a timestamp \u2014 is inside \`content\`. Move it to \`transport\`.`
|
|
14698
|
+
);
|
|
14699
|
+
}
|
|
14700
|
+
const other = contentIdentity(await input.differentRecords());
|
|
14701
|
+
if (other === first) {
|
|
14702
|
+
throw new Error(
|
|
14703
|
+
`content identity is NOT discriminating${where}: a call returning different records hashed to the same value (${other}). Records that distinguish one result from another belong in \`content\`, not \`transport\`.`
|
|
14704
|
+
);
|
|
14705
|
+
}
|
|
14706
|
+
};
|
|
14707
|
+
|
|
14492
14708
|
// src/email-attachments.ts
|
|
14493
|
-
import { createHash, randomBytes } from "crypto";
|
|
14709
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
14494
14710
|
var GMAIL_ATTACHMENT_LIMITS = {
|
|
14495
14711
|
provider: "Gmail",
|
|
14496
14712
|
maxTotalRawBytes: 18 * 1024 * 1024
|
|
@@ -14642,7 +14858,7 @@ var assembleMimeMessage = (input, attachments) => {
|
|
|
14642
14858
|
return [...headers, "", parts.join(CRLF)].join(CRLF);
|
|
14643
14859
|
};
|
|
14644
14860
|
var sendMessageId = (input) => {
|
|
14645
|
-
const attachmentDigest = input.attachments.map((a) =>
|
|
14861
|
+
const attachmentDigest = input.attachments.map((a) => createHash2("sha256").update(a.bytes).digest("hex")).join(",");
|
|
14646
14862
|
const idSeed = [
|
|
14647
14863
|
input.workspaceId,
|
|
14648
14864
|
[...input.to].sort().join(","),
|
|
@@ -14653,7 +14869,7 @@ var sendMessageId = (input) => {
|
|
|
14653
14869
|
input.html ?? "",
|
|
14654
14870
|
attachmentDigest
|
|
14655
14871
|
].join("|");
|
|
14656
|
-
const hash2 =
|
|
14872
|
+
const hash2 = createHash2("sha256").update(idSeed).digest("hex");
|
|
14657
14873
|
return { hash: hash2, messageId: `<${hash2}@mission.send>` };
|
|
14658
14874
|
};
|
|
14659
14875
|
var EmailAttachments = {
|
|
@@ -14670,7 +14886,7 @@ var EmailAttachments = {
|
|
|
14670
14886
|
};
|
|
14671
14887
|
|
|
14672
14888
|
// src/files.ts
|
|
14673
|
-
import { createHash as
|
|
14889
|
+
import { createHash as createHash3 } from "crypto";
|
|
14674
14890
|
var FileTransferTimeoutError = class extends Error {
|
|
14675
14891
|
constructor(operation) {
|
|
14676
14892
|
super(`File transfer timed out: ${operation}`);
|
|
@@ -14736,7 +14952,7 @@ function createFilesModule(api2) {
|
|
|
14736
14952
|
};
|
|
14737
14953
|
const stageInboundBytes = async (input) => {
|
|
14738
14954
|
const buffer2 = Buffer.isBuffer(input.bytes) ? input.bytes : Buffer.from(input.bytes);
|
|
14739
|
-
const sha256 =
|
|
14955
|
+
const sha256 = createHash3("sha256").update(buffer2).digest("hex");
|
|
14740
14956
|
const contentType = normalizeContentType(input.contentType);
|
|
14741
14957
|
const filename = sanitizeStagedName(input.filename);
|
|
14742
14958
|
const { uploadUrl, contentLengthRange, key } = await api2.trpc.skill.requestStagingUpload.mutate({
|
|
@@ -15836,9 +16052,14 @@ export {
|
|
|
15836
16052
|
FileTransferTimeoutError,
|
|
15837
16053
|
MAX_STAGING_UPLOAD_BYTES,
|
|
15838
16054
|
SkillToolHttpError,
|
|
16055
|
+
TOOL_RESULT_TRANSPORT_MAX_BYTES,
|
|
16056
|
+
assertContentIdentityContractLoaded,
|
|
16057
|
+
assertContentIdentityStable,
|
|
15839
16058
|
clearDebugLog,
|
|
16059
|
+
contentIdentity,
|
|
15840
16060
|
createMissionSDK,
|
|
15841
16061
|
debugLog,
|
|
15842
|
-
fileRefSchema
|
|
16062
|
+
fileRefSchema,
|
|
16063
|
+
toolResult
|
|
15843
16064
|
};
|
|
15844
16065
|
//# sourceMappingURL=index.js.map
|