@deepstrike/wasm 0.2.77 → 0.2.79
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/agent.d.ts +6 -2
- package/dist/agent.js +59 -19
- package/dist/index.d.ts +7 -9
- package/dist/index.js +3 -5
- package/dist/memory/attestation.d.ts +38 -0
- package/dist/memory/attestation.js +43 -0
- package/dist/memory/authority.d.ts +2 -0
- package/dist/memory/authority.js +3 -1
- package/dist/memory/candidate-sink.d.ts +66 -0
- package/dist/memory/candidate-sink.js +82 -0
- package/dist/memory/durable.d.ts +4 -2
- package/dist/memory/durable.js +2 -0
- package/dist/memory/index.d.ts +9 -1
- package/dist/memory/index.js +5 -0
- package/dist/memory/lifecycle.d.ts +82 -0
- package/dist/memory/lifecycle.js +46 -0
- package/dist/memory/recall-projection.d.ts +5 -0
- package/dist/memory/recall-projection.js +18 -0
- package/dist/memory/runtime-policy.d.ts +15 -0
- package/dist/memory/runtime-policy.js +26 -0
- package/dist/memory/writer.d.ts +22 -0
- package/dist/memory/writer.js +41 -0
- package/dist/providers/anthropic-compaction.d.ts +84 -0
- package/dist/providers/anthropic-compaction.js +222 -0
- package/dist/providers/anthropic.d.ts +41 -1
- package/dist/providers/anthropic.js +187 -18
- package/dist/providers/base.js +11 -1
- package/dist/runtime/canonical-kernel-step.d.ts +1 -2
- package/dist/runtime/canonical-kernel-step.js +74 -8
- package/dist/runtime/execution-plane.js +1 -1
- package/dist/runtime/index.d.ts +0 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/kernel-event-log.d.ts +8 -0
- package/dist/runtime/kernel-event-log.js +63 -1
- package/dist/runtime/kernel-step.d.ts +15 -2
- package/dist/runtime/os-snapshot.js +1 -0
- package/dist/runtime/runner.d.ts +44 -26
- package/dist/runtime/runner.js +206 -240
- package/dist/runtime/session-event-registry.generated.d.ts +212 -0
- package/dist/runtime/session-event-registry.generated.js +112 -0
- package/dist/runtime/session-log.d.ts +83 -16
- package/dist/runtime/session-log.js +10 -58
- package/dist/runtime/session-repair.d.ts +0 -3
- package/dist/runtime/session-repair.js +0 -3
- package/dist/runtime/sub-agent-orchestrator.d.ts +5 -1
- package/dist/runtime/sub-agent-orchestrator.js +4 -0
- package/dist/runtime/types/agent.d.ts +18 -52
- package/dist/runtime/types/agent.js +8 -45
- package/dist/types.d.ts +10 -5
- package/package.json +3 -3
- package/dist/memory/extraction.d.ts +0 -4
- package/dist/memory/extraction.js +0 -48
- package/dist/runtime/context.d.ts +0 -28
- package/dist/runtime/context.js +0 -16
- package/dist/runtime/evolution.d.ts +0 -189
- package/dist/runtime/evolution.js +0 -25
- package/dist/runtime/reducers.d.ts +0 -15
- package/dist/runtime/reducers.js +0 -57
- package/dist/runtime/verifiable-report.d.ts +0 -59
- package/dist/runtime/verifiable-report.js +0 -64
- package/dist/runtime/workflow-control-flow.d.ts +0 -21
- package/dist/runtime/workflow-control-flow.js +0 -83
package/dist/runtime/runner.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { prepareProviderRequest } from "../providers/prepared-request.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { extractSessionMemories } from "../memory/extraction.js";
|
|
2
|
+
import { resolveMemoryRuntimePolicy } from "../memory/runtime-policy.js";
|
|
3
|
+
import { memoryCandidateId } from "../memory/candidate-sink.js";
|
|
5
4
|
import { resolvePermissionRequest } from "./execution-plane.js";
|
|
6
5
|
import { governancePolicyToKernelEvent } from "../governance.js";
|
|
7
6
|
import { parseToolCallArguments } from "./tool-arguments.js";
|
|
8
7
|
import { stripSkillFrontmatter } from "./execution-plane.js";
|
|
9
8
|
import { getKernel } from "./kernel.js";
|
|
10
9
|
import { checkMemoryWrite, deriveMemoryRecall } from "../memory/authority.js";
|
|
10
|
+
import { memoryFetchRecall } from "../memory/recall-projection.js";
|
|
11
|
+
import { MemoryWriter } from "../memory/writer.js";
|
|
11
12
|
import { CanonicalRunnerRuntime, canonicalKernelAction, canonicalKernelApply, isInvalidInputError, canonicalKernelMaybeAction, canonicalStartAgent, canonicalStartWorkflow, sha256 as canonicalSha256, } from "./canonical-kernel-step.js";
|
|
12
13
|
import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
|
|
13
14
|
import { sanitizeReplayText } from "./replay-sanitize.js";
|
|
@@ -18,8 +19,6 @@ import { messageToKernelMessage, archivePresentationFromObservations, skillMetad
|
|
|
18
19
|
import { agentRunSpecToKernel, MILESTONE_UNVERIFIED_REASON, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowBudgetNote, workflowNodeSpecToKernel, workflowNodeOutcomeFromKernel, workflowNodeStatusFromTermination, workflowNodeToManifest, workflowNodeToSpec, workflowSpecToKernel, } from "./types/agent.js";
|
|
19
20
|
import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
20
21
|
import { extractJsonValue, schemaInstruction, schemaRetryInstruction, validateAgainstSchema, } from "./output-schema.js";
|
|
21
|
-
import { resolveReducer } from "./reducers.js";
|
|
22
|
-
import { loopInstruction, classifyInstruction, judgeGoal, dependencyOutputsNote, extractClassifyBranch, extractJudgeWinner, } from "./workflow-control-flow.js";
|
|
23
22
|
import { kernelObservationToSessionEvent } from "./kernel-event-log.js";
|
|
24
23
|
import { assertNativeProfile } from "./os-profile.js";
|
|
25
24
|
import { classifyProviderError, providerErrorEventFields } from "../providers/provider-error.js";
|
|
@@ -107,8 +106,19 @@ function controlRequestRejection(observations, operation) {
|
|
|
107
106
|
reason: typeof rejected.reason === "string" ? rejected.reason : "request denied",
|
|
108
107
|
};
|
|
109
108
|
}
|
|
109
|
+
/** The kernel's wire form of the tiers a provider performs natively, or nothing when it performs
|
|
110
|
+
* none — an operation that delegates nothing configures exactly as before. */
|
|
111
|
+
export function delegatedContextTiers(native) {
|
|
112
|
+
if (!native?.toolResultClearing && !native?.compaction)
|
|
113
|
+
return undefined;
|
|
114
|
+
return {
|
|
115
|
+
...(native.toolResultClearing ? { tool_result_clearing: true } : {}),
|
|
116
|
+
...(native.compaction ? { compaction: true } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
110
119
|
export class RuntimeRunner {
|
|
111
120
|
opts;
|
|
121
|
+
memoryRuntimeGates;
|
|
112
122
|
interrupted = false;
|
|
113
123
|
cancellationReason;
|
|
114
124
|
/** #2-B-ii: aborts the in-flight provider stream on interrupt/preempt. Recreated per `execute`. */
|
|
@@ -147,6 +157,10 @@ export class RuntimeRunner {
|
|
|
147
157
|
activeProviderInvocationId;
|
|
148
158
|
constructor(opts) {
|
|
149
159
|
this.opts = opts;
|
|
160
|
+
this.memoryRuntimeGates = resolveMemoryRuntimePolicy(opts.memoryRuntimePolicy);
|
|
161
|
+
if (this.memoryPathToCandidateSink("modelWrites") && !opts.memoryCandidateSink) {
|
|
162
|
+
throw new Error('memory runtime policy modelWrites = "candidate_sink" needs RuntimeOptions.memoryCandidateSink');
|
|
163
|
+
}
|
|
150
164
|
const schemaAttempts = opts.workflowSchemaValidationAttempts ?? 2;
|
|
151
165
|
if (!Number.isInteger(schemaAttempts) || schemaAttempts < 1 || schemaAttempts > 16) {
|
|
152
166
|
throw new RangeError("workflowSchemaValidationAttempts must be an integer between 1 and 16");
|
|
@@ -181,7 +195,6 @@ export class RuntimeRunner {
|
|
|
181
195
|
maxTurns: this.opts.maxTurns,
|
|
182
196
|
maxTotalTokens: this.opts.maxTotalTokens,
|
|
183
197
|
maxWallMs: this.opts.timeoutMs,
|
|
184
|
-
artifactSetDigest: this.opts.artifactSetDigest,
|
|
185
198
|
memoryBindingId: `wasm-memory-${this.opts.agentId ?? "root"}`,
|
|
186
199
|
persistPayload: async (_callId, content, previewBytes) => {
|
|
187
200
|
const digest = canonicalSha256(content);
|
|
@@ -346,9 +359,9 @@ export class RuntimeRunner {
|
|
|
346
359
|
* §22.13 · a host-authored memory write. The kernel decides whether it may happen: a live
|
|
347
360
|
* operation answers through `admit_memory_write` (the model's validation rule and rolling write
|
|
348
361
|
* quota); with no live operation the same kernel rule answers statelessly. Resolves `true` when
|
|
349
|
-
* the record was persisted.
|
|
362
|
+
* the record was persisted or submitted as a pending candidate.
|
|
350
363
|
*/
|
|
351
|
-
async writeMemory(memory, sessionId) {
|
|
364
|
+
async writeMemory(memory, sessionId, candidate) {
|
|
352
365
|
if (!this.opts.memoryStore || !this.opts.agentId)
|
|
353
366
|
return false;
|
|
354
367
|
const turn = this.activeKernel?.turn() ?? 0;
|
|
@@ -361,6 +374,12 @@ export class RuntimeRunner {
|
|
|
361
374
|
}
|
|
362
375
|
return false;
|
|
363
376
|
}
|
|
377
|
+
if (candidate) {
|
|
378
|
+
const receipt = await this.memoryWriter().propose(this.opts.agentId, { candidate_id: candidate.candidateId, source: candidate.source, record: memory });
|
|
379
|
+
if (sessionId)
|
|
380
|
+
await this.opts.sessionLog.append(sessionId, this.memoryCandidateEvent(receipt, candidate.source, memory));
|
|
381
|
+
return receipt.status !== "rejected";
|
|
382
|
+
}
|
|
364
383
|
try {
|
|
365
384
|
await this.opts.memoryStore.put(this.opts.agentId, memory);
|
|
366
385
|
}
|
|
@@ -376,6 +395,114 @@ export class RuntimeRunner {
|
|
|
376
395
|
}
|
|
377
396
|
return true;
|
|
378
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* M4 · a host-initiated recall (an explicit `recall`). Same path as the prefetch: the retrieval
|
|
400
|
+
* gate, the policy's `retrievalTopK`, the kernel-derived recall lifecycle, and a
|
|
401
|
+
* `memory_retrieval_result` audit.
|
|
402
|
+
*/
|
|
403
|
+
async queryMemory(query, sessionId) {
|
|
404
|
+
if (!this.opts.memoryStore || !this.opts.agentId)
|
|
405
|
+
return [];
|
|
406
|
+
if (!this.memoryPathEnabled("retrievalEnabled"))
|
|
407
|
+
return [];
|
|
408
|
+
const policyLimit = this.opts.memoryPolicy?.retrievalTopK;
|
|
409
|
+
const limit = policyLimit === undefined ? query.top_k : Math.min(query.top_k, policyLimit);
|
|
410
|
+
if (limit <= 0)
|
|
411
|
+
return [];
|
|
412
|
+
const hits = (await this.opts.memoryStore.search(this.opts.agentId, { ...query, top_k: limit })).slice(0, limit);
|
|
413
|
+
await this.recordHostMemoryRecall(hits);
|
|
414
|
+
if (sessionId)
|
|
415
|
+
await this.opts.sessionLog.append(sessionId, { kind: "memory_retrieval_result", hits });
|
|
416
|
+
return hits;
|
|
417
|
+
}
|
|
418
|
+
async persistMemoryEffectResult(operationId, effectId, canonicalMemory) {
|
|
419
|
+
const agentId = this.opts.agentId;
|
|
420
|
+
const record = {
|
|
421
|
+
record_id: `memory:${crypto.randomUUID()}`,
|
|
422
|
+
scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: agentId ?? "default" },
|
|
423
|
+
name: String(canonicalMemory.name ?? ""),
|
|
424
|
+
kind: String(canonicalMemory.kind ?? "reference"),
|
|
425
|
+
content: String(canonicalMemory.content ?? ""),
|
|
426
|
+
description: String(canonicalMemory.description ?? ""),
|
|
427
|
+
provenance: {
|
|
428
|
+
author: "model",
|
|
429
|
+
trust: "untrusted",
|
|
430
|
+
evidence_refs: Array.isArray(canonicalMemory.evidence_refs)
|
|
431
|
+
? canonicalMemory.evidence_refs.map(String)
|
|
432
|
+
: [],
|
|
433
|
+
},
|
|
434
|
+
created_at: Number(canonicalMemory.accepted_at_ms ?? Date.now()),
|
|
435
|
+
updated_at: Number(canonicalMemory.accepted_at_ms ?? Date.now()),
|
|
436
|
+
recall_count: 0,
|
|
437
|
+
confidence: 1,
|
|
438
|
+
links: [],
|
|
439
|
+
pinned: false,
|
|
440
|
+
};
|
|
441
|
+
if (this.memoryPathToCandidateSink("modelWrites")) {
|
|
442
|
+
try {
|
|
443
|
+
if (!agentId)
|
|
444
|
+
throw new Error("memory persistence requires RuntimeOptions.agentId");
|
|
445
|
+
const modelProposal = Boolean(canonicalMemory.causation && typeof canonicalMemory.causation === "object" && canonicalMemory.causation.kind === "provider_tool");
|
|
446
|
+
const source = modelProposal ? "model_proposal" : "child_request";
|
|
447
|
+
// Child requests keep their v2 identity so in-flight redeliveries still match the sink.
|
|
448
|
+
const candidateId = modelProposal
|
|
449
|
+
? await memoryCandidateId(["v3", agentId, "model_proposal", operationId, effectId, "0"])
|
|
450
|
+
: await memoryCandidateId(["v2", agentId, "child_request", operationId, effectId, "0"]);
|
|
451
|
+
const receipt = await this.memoryWriter().propose(agentId, { candidate_id: candidateId, source, record });
|
|
452
|
+
return { kind: "memory_candidate_result", effect_id: effectId, ...receipt };
|
|
453
|
+
}
|
|
454
|
+
catch (cause) {
|
|
455
|
+
return { kind: "memory_candidate_result", effect_id: effectId, error: formatToolError(cause) };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
if (!this.memoryPathEnabled("modelWritesEnabled")) {
|
|
460
|
+
throw new Error("model memory writes are disabled by memoryRuntimePolicy.modelWrites");
|
|
461
|
+
}
|
|
462
|
+
if (!agentId)
|
|
463
|
+
throw new Error("memory persistence requires RuntimeOptions.agentId");
|
|
464
|
+
// The receipt names what the store now holds: the record's id and its content digest.
|
|
465
|
+
const receipt = await this.memoryWriter().persist(agentId, record);
|
|
466
|
+
return { kind: "memory_persist_result", effect_id: effectId, ...receipt };
|
|
467
|
+
}
|
|
468
|
+
catch (cause) {
|
|
469
|
+
return { kind: "memory_persist_result", effect_id: effectId, error: formatToolError(cause) };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/** The host's side of every memory write: the store, or the candidate sink's receipt contract. */
|
|
473
|
+
memoryWriter() {
|
|
474
|
+
return new MemoryWriter({ store: this.opts.memoryStore, sink: this.opts.memoryCandidateSink });
|
|
475
|
+
}
|
|
476
|
+
/** The audit fact for a sink's answer. Only an accepted candidate is a written record. */
|
|
477
|
+
memoryCandidateEvent(receipt, source, memory) {
|
|
478
|
+
const turn = this.activeKernel?.turn() ?? 0;
|
|
479
|
+
switch (receipt.status) {
|
|
480
|
+
case "pending":
|
|
481
|
+
return {
|
|
482
|
+
kind: "memory_candidate_pending", turn, candidate_id: receipt.candidate_id, source,
|
|
483
|
+
name: memory.name, receipt_id: receipt.receipt_id,
|
|
484
|
+
};
|
|
485
|
+
case "rejected":
|
|
486
|
+
return {
|
|
487
|
+
kind: "memory_candidate_rejected", turn, candidate_id: receipt.candidate_id, source,
|
|
488
|
+
name: memory.name, receipt_id: receipt.receipt_id, reason_code: receipt.reason_code,
|
|
489
|
+
};
|
|
490
|
+
case "accepted":
|
|
491
|
+
return {
|
|
492
|
+
kind: "memory_written", turn, record_id: receipt.record_ref, scope: memory.scope,
|
|
493
|
+
memory_kind: memory.kind, name: memory.name, size_bytes: new TextEncoder().encode(memory.content).byteLength,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/** Host memory-policy gate: a path is on unless the host set its switch to `"disabled"`. */
|
|
498
|
+
memoryPathEnabled(path) {
|
|
499
|
+
return this.memoryRuntimeGates?.[path] ?? true;
|
|
500
|
+
}
|
|
501
|
+
/** M2 · whether a write path hands its writes to the candidate sink. Off unless the host set the
|
|
502
|
+
* path to `"candidate_sink"` — unlike the on/off gates, the default here is the store. */
|
|
503
|
+
memoryPathToCandidateSink(path) {
|
|
504
|
+
return this.memoryRuntimeGates?.[`${path}ToCandidateSink`] ?? false;
|
|
505
|
+
}
|
|
379
506
|
async admitMemoryWrite(memory) {
|
|
380
507
|
const runtime = this.activeKernel;
|
|
381
508
|
if (runtime && runtime.hasStarted() && !runtime.isTerminal()) {
|
|
@@ -624,6 +751,22 @@ export class RuntimeRunner {
|
|
|
624
751
|
tokens: Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)),
|
|
625
752
|
});
|
|
626
753
|
}
|
|
754
|
+
for (const [agentId, content] of Object.entries(this.opts.dependencyOutputs ?? {})) {
|
|
755
|
+
await this.commitKernelApply(runtime, this.pendingObservations, {
|
|
756
|
+
kind: "add_dependency_output",
|
|
757
|
+
agent_id: agentId,
|
|
758
|
+
content,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
for (const entry of this.opts.inheritedKnowledge ?? []) {
|
|
762
|
+
await this.commitKernelApply(runtime, this.pendingObservations, {
|
|
763
|
+
kind: "add_knowledge_message",
|
|
764
|
+
content: entry.content,
|
|
765
|
+
...(entry.key ? { key: entry.key } : {}),
|
|
766
|
+
tokens: entry.tokens ?? Math.max(1, Math.ceil(entry.content.length / 4)),
|
|
767
|
+
...(entry.pinned ? { pinned: true } : {}),
|
|
768
|
+
});
|
|
769
|
+
}
|
|
627
770
|
if (this.opts.initialMemory) {
|
|
628
771
|
for (const mem of this.opts.initialMemory) {
|
|
629
772
|
await this.commitKernelApply(runtime, this.pendingObservations, {
|
|
@@ -652,7 +795,7 @@ export class RuntimeRunner {
|
|
|
652
795
|
});
|
|
653
796
|
}
|
|
654
797
|
if (this.opts.memoryStore && this.opts.agentId) {
|
|
655
|
-
await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
|
|
798
|
+
await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true, read: this.memoryPathEnabled("retrievalEnabled"), write: this.memoryPathEnabled("modelWritesEnabled"), propose_memory: Boolean(this.opts.memoryCandidateSink && this.memoryPathToCandidateSink("modelWrites")), recall: Boolean(this.opts.memoryScope) });
|
|
656
799
|
}
|
|
657
800
|
if (this.opts.knowledgeSource) {
|
|
658
801
|
await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_knowledge_enabled", enabled: true });
|
|
@@ -748,14 +891,9 @@ export class RuntimeRunner {
|
|
|
748
891
|
}
|
|
749
892
|
if (!resumeMidRun)
|
|
750
893
|
await this.applyKernelPolicies(runtime);
|
|
751
|
-
// I4: pre-fetch memory before the first LLM turn (mirrors Node).
|
|
752
|
-
//
|
|
753
|
-
// ordinary `memory` tool result, so it decays with the compression pyramid instead of
|
|
754
|
-
// pinning itself in `knowledge` forever.
|
|
894
|
+
// I4: pre-fetch memory before the first LLM turn (mirrors Node). Each hit is an unpinned keyed
|
|
895
|
+
// `memory:<record_id>` knowledge entry, evicted at a boundary when usage needs the room.
|
|
755
896
|
this.currentGoal = goal;
|
|
756
|
-
if (!resumeMidRun) {
|
|
757
|
-
await this.prefetchMemoryIntoHistory(runtime, "initial");
|
|
758
|
-
}
|
|
759
897
|
let action = resumeMidRun
|
|
760
898
|
? runtime.resumeAction() ?? (() => { throw new Error("restored canonical operation has no pending effect or terminal"); })()
|
|
761
899
|
: await canonicalStartAgent(runtime, this.pendingObservations, startTask, startRunSpec);
|
|
@@ -885,18 +1023,6 @@ export class RuntimeRunner {
|
|
|
885
1023
|
this.providerRetryPending = action.kind === "call_provider";
|
|
886
1024
|
continue;
|
|
887
1025
|
}
|
|
888
|
-
if (!promptMeasurement)
|
|
889
|
-
throw new Error("context preparation requires prompt measurement");
|
|
890
|
-
const contextAdapter = await createNativeContextPreparationAdapter();
|
|
891
|
-
const preparation = contextAdapter.prepare({
|
|
892
|
-
effect: action.contextEffect,
|
|
893
|
-
request_fingerprint: providerPlan.fingerprint,
|
|
894
|
-
provider_route: { ...this.providerRoute, request_fingerprint_scope: preparedRequest.scope },
|
|
895
|
-
prompt_measurement: promptMeasurement,
|
|
896
|
-
});
|
|
897
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
898
|
-
kind: "context_prepared", turn: runtime.turn(), effect_id: providerEffectId, preparation,
|
|
899
|
-
});
|
|
900
1026
|
const abortSignal = this.abortController?.signal;
|
|
901
1027
|
try {
|
|
902
1028
|
for await (const evt of preparedRequest.stream(abortSignal)) {
|
|
@@ -1122,56 +1248,28 @@ export class RuntimeRunner {
|
|
|
1122
1248
|
});
|
|
1123
1249
|
}
|
|
1124
1250
|
else if (action.kind === "persist_memory") {
|
|
1125
|
-
|
|
1126
|
-
// build the full record here, and let the receipt name exactly what the store now holds.
|
|
1127
|
-
const canonicalMemory = action.memory;
|
|
1128
|
-
const acceptedAt = Number(canonicalMemory.accepted_at_ms ?? Date.now());
|
|
1129
|
-
const record = {
|
|
1130
|
-
record_id: `memory:${crypto.randomUUID()}`,
|
|
1131
|
-
scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: this.opts.agentId ?? "default" },
|
|
1132
|
-
name: String(canonicalMemory.name ?? ""),
|
|
1133
|
-
kind: String(canonicalMemory.kind ?? "reference"),
|
|
1134
|
-
content: String(canonicalMemory.content ?? ""),
|
|
1135
|
-
description: String(canonicalMemory.description ?? ""),
|
|
1136
|
-
provenance: {
|
|
1137
|
-
author: "model",
|
|
1138
|
-
trust: "untrusted",
|
|
1139
|
-
evidence_refs: Array.isArray(canonicalMemory.evidence_refs) ? canonicalMemory.evidence_refs.map(String) : [],
|
|
1140
|
-
},
|
|
1141
|
-
created_at: acceptedAt,
|
|
1142
|
-
updated_at: acceptedAt,
|
|
1143
|
-
recall_count: 0,
|
|
1144
|
-
confidence: 1,
|
|
1145
|
-
links: [],
|
|
1146
|
-
pinned: false,
|
|
1147
|
-
};
|
|
1148
|
-
let error;
|
|
1149
|
-
try {
|
|
1150
|
-
if (!this.opts.memoryStore || !this.opts.agentId)
|
|
1151
|
-
throw new Error("WASM memory persistence requires memoryStore and agentId");
|
|
1152
|
-
await this.opts.memoryStore.put(this.opts.agentId, record);
|
|
1153
|
-
}
|
|
1154
|
-
catch (cause) {
|
|
1155
|
-
error = formatToolError(cause);
|
|
1156
|
-
}
|
|
1157
|
-
action = await this.commitKernelAction(runtime, this.pendingObservations, {
|
|
1158
|
-
kind: "memory_persist_result",
|
|
1159
|
-
effect_id: action.effectId,
|
|
1160
|
-
...(error ? { error } : {
|
|
1161
|
-
record_ref: record.record_id,
|
|
1162
|
-
digest: `sha256:${sha256Hex(record.content)}`,
|
|
1163
|
-
}),
|
|
1164
|
-
});
|
|
1251
|
+
action = await this.commitKernelAction(runtime, this.pendingObservations, await this.persistMemoryEffectResult(runtime.operationId, action.effectId, action.memory));
|
|
1165
1252
|
}
|
|
1166
1253
|
else if (action.kind === "query_memory") {
|
|
1167
1254
|
let hits = [];
|
|
1168
1255
|
let error;
|
|
1169
1256
|
try {
|
|
1257
|
+
if (!this.memoryPathEnabled("retrievalEnabled"))
|
|
1258
|
+
throw new Error("memory retrieval is disabled by memoryRuntimePolicy.retrieval");
|
|
1170
1259
|
if (!this.opts.memoryStore || !this.opts.agentId)
|
|
1171
1260
|
throw new Error("WASM memory queries require memoryStore and agentId");
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1261
|
+
const recordId = typeof action.query.record_id === "string" ? action.query.record_id : undefined;
|
|
1262
|
+
const causation = action.query.causation;
|
|
1263
|
+
hits = causation?.kind === "kernel_recall"
|
|
1264
|
+
? await this.kernelRecallHits(String(action.query.text ?? ""), action.requestedK, causation.sprint ? "renewal" : "initial")
|
|
1265
|
+
: recordId === undefined
|
|
1266
|
+
? await this.opts.memoryStore.search(this.opts.agentId, {
|
|
1267
|
+
...action.query, top_k: action.requestedK,
|
|
1268
|
+
})
|
|
1269
|
+
: memoryFetchRecall(await this.opts.memoryStore.get(this.opts.agentId, recordId), {
|
|
1270
|
+
scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: this.opts.agentId },
|
|
1271
|
+
kinds: Array.isArray(action.query.kinds) ? action.query.kinds.map(String) : [],
|
|
1272
|
+
}, Date.now());
|
|
1175
1273
|
}
|
|
1176
1274
|
catch (cause) {
|
|
1177
1275
|
error = formatToolError(cause);
|
|
@@ -1183,6 +1281,9 @@ export class RuntimeRunner {
|
|
|
1183
1281
|
hits,
|
|
1184
1282
|
...(error ? { error } : {}),
|
|
1185
1283
|
});
|
|
1284
|
+
if (!error && hits.length > 0 && this.currentSessionId) {
|
|
1285
|
+
await this.opts.sessionLog.append(this.currentSessionId, { kind: "memory_retrieval_result", hits });
|
|
1286
|
+
}
|
|
1186
1287
|
// M3/M4: mirror the kernel's journaled recall lifecycle + surface promotion suggestions.
|
|
1187
1288
|
for (const obs of this.pendingObservations.slice(obsStart)) {
|
|
1188
1289
|
if (obs.kind === "memory_recalled" && obs.recalls?.length && this.opts.agentId) {
|
|
@@ -1196,7 +1297,7 @@ export class RuntimeRunner {
|
|
|
1196
1297
|
else if (action.kind === "archive_page_out") {
|
|
1197
1298
|
const archiveMeta = this.activePageOutArchive
|
|
1198
1299
|
?? this.pendingPageOutArchives.shift()
|
|
1199
|
-
?? { archiveStart: this.nextArchiveStart, compressedSeq: await this.opts.sessionLog.latestSeq(sessionId) };
|
|
1300
|
+
?? { archiveStart: this.nextArchiveStart, compressedSeq: await this.opts.sessionLog.latestSeq(sessionId), action: action.action, tier: action.tier };
|
|
1200
1301
|
this.activePageOutArchive = archiveMeta;
|
|
1201
1302
|
let archiveRef;
|
|
1202
1303
|
let error;
|
|
@@ -1212,9 +1313,6 @@ export class RuntimeRunner {
|
|
|
1212
1313
|
catch (cause) {
|
|
1213
1314
|
error = formatToolError(cause);
|
|
1214
1315
|
}
|
|
1215
|
-
const archived = action.archived;
|
|
1216
|
-
const archiveAction = archiveMeta.action ?? "auto_compact";
|
|
1217
|
-
const archiveTier = archiveMeta.tier;
|
|
1218
1316
|
if (!error)
|
|
1219
1317
|
this.activePageOutArchive = undefined;
|
|
1220
1318
|
action = await this.commitKernelAction(runtime, this.pendingObservations, {
|
|
@@ -1223,9 +1321,6 @@ export class RuntimeRunner {
|
|
|
1223
1321
|
...(archiveRef ? { archive_ref: archiveRef } : {}),
|
|
1224
1322
|
...(error ? { error } : {}),
|
|
1225
1323
|
});
|
|
1226
|
-
if (!error && archiveTier === "semantic" && archived.length > 0) {
|
|
1227
|
-
void this.archiveSemanticPageOut(archived, archiveAction);
|
|
1228
|
-
}
|
|
1229
1324
|
}
|
|
1230
1325
|
else if (action.kind === "load_payload") {
|
|
1231
1326
|
const content = await this.payloadStore().loadPayload(sessionId, action.payloadRef);
|
|
@@ -1552,11 +1647,6 @@ export class RuntimeRunner {
|
|
|
1552
1647
|
updatedAtMs: Date.now(),
|
|
1553
1648
|
};
|
|
1554
1649
|
await this.opts.memoryStore.saveSession(completedSession);
|
|
1555
|
-
if (this.opts.memoryScope) {
|
|
1556
|
-
const extracted = await extractSessionMemories(this.opts.memoryProvider ?? this.opts.provider, completedSession, this.opts.memoryScope, this.opts.memorySystemPrompt);
|
|
1557
|
-
for (const memory of extracted)
|
|
1558
|
-
await this.writeMemory(memory, sessionId);
|
|
1559
|
-
}
|
|
1560
1650
|
}
|
|
1561
1651
|
catch { /* non-fatal */ }
|
|
1562
1652
|
}
|
|
@@ -1579,24 +1669,21 @@ export class RuntimeRunner {
|
|
|
1579
1669
|
* fed back on mismatch. If it still does not conform, the node is failed with the validation
|
|
1580
1670
|
* reason (an `Error`-terminated result fails the node in-kernel, starving its dependents).
|
|
1581
1671
|
*/
|
|
1582
|
-
async runWorkflowNode(node, parentSessionId, orchestrator, budget,
|
|
1583
|
-
// G2: a reduce node runs no LLM — execute the registered pure function over its dependency
|
|
1584
|
-
// outputs and feed the result back as an ordinary completion. Deterministic; no agent burned.
|
|
1585
|
-
if (node.reducer) {
|
|
1586
|
-
return this.runReduceNode(node, outputs ?? new Map());
|
|
1587
|
-
}
|
|
1672
|
+
async runWorkflowNode(node, parentSessionId, orchestrator, budget, abortSignal) {
|
|
1588
1673
|
const baseSpec = workflowNodeToSpec(node, parentSessionId);
|
|
1589
1674
|
const manifest = workflowNodeToManifest(node, parentSessionId);
|
|
1590
1675
|
// G4: surface remaining workflow budget so a coordinator node can size its submission.
|
|
1591
1676
|
const budgetNote = workflowBudgetNote(budget);
|
|
1592
|
-
//
|
|
1593
|
-
|
|
1594
|
-
const depsNote = dependencyOutputsNote(node.input_agent_ids, outputs);
|
|
1595
|
-
const withBudget = (goal) => [goal, depsNote, budgetNote].filter(Boolean).join("\n\n");
|
|
1677
|
+
// A DAG edge carries data: the kernel composes the dependency section of the brief.
|
|
1678
|
+
const withBudget = (goal) => [goal, node.dependency_brief, budgetNote].filter(Boolean).join("\n\n");
|
|
1596
1679
|
const mkCtx = (goal) => ({
|
|
1597
1680
|
parentOpts: this.opts,
|
|
1598
1681
|
parentSessionId,
|
|
1599
1682
|
spec: { ...baseSpec, goal: withBudget(goal) },
|
|
1683
|
+
...(node.dependency_outputs
|
|
1684
|
+
? { dependencyOutputs: node.dependency_outputs }
|
|
1685
|
+
: {}),
|
|
1686
|
+
...(node.inherited_knowledge?.length ? { inheritedKnowledge: node.inherited_knowledge } : {}),
|
|
1600
1687
|
manifest,
|
|
1601
1688
|
sessionLog: this.opts.sessionLog,
|
|
1602
1689
|
// This child is a workflow node, so capability resolution applies workflow-node quarantine
|
|
@@ -1609,39 +1696,6 @@ export class RuntimeRunner {
|
|
|
1609
1696
|
// #2-B-ii: the per-node abort signal the driver fires when the kernel preempts this node.
|
|
1610
1697
|
...(abortSignal ? { abortSignal } : {}),
|
|
1611
1698
|
});
|
|
1612
|
-
const textOf = (r) => {
|
|
1613
|
-
const c = r.result.finalMessage?.content;
|
|
1614
|
-
return typeof c === "string" ? c : c != null ? JSON.stringify(c) : "";
|
|
1615
|
-
};
|
|
1616
|
-
const withSignal = (r, patch) => ({ ...r, result: { ...r.result, ...patch } });
|
|
1617
|
-
// A#2 tournament judge: compare two entrants' produced outputs rather than running the node's own
|
|
1618
|
-
// goal. Look up both candidates, judge over the controller's criterion, and report the winner's id.
|
|
1619
|
-
if (node.judge_match) {
|
|
1620
|
-
const out = outputs ?? new Map();
|
|
1621
|
-
const left = out.get(node.judge_match.left) ?? "";
|
|
1622
|
-
const right = out.get(node.judge_match.right) ?? "";
|
|
1623
|
-
const result = await orchestrator.run(mkCtx(judgeGoal(baseSpec.goal, left, right)));
|
|
1624
|
-
const winner = extractJudgeWinner(textOf(result));
|
|
1625
|
-
const winnerId = winner === "right" ? node.judge_match.right : node.judge_match.left;
|
|
1626
|
-
return withSignal(result, { tournamentWinner: winnerId });
|
|
1627
|
-
}
|
|
1628
|
-
// loop-control loop iteration: run the increment under the armed pacing trap (workflowNodeToSpec set
|
|
1629
|
-
// `loopRound`, and the iteration resumes the loop's stable session — transcript-as-carry).
|
|
1630
|
-
// The kernel-adjudicated `pace` verb is the only continuation signal. Ending without a pace
|
|
1631
|
-
// decision completes the loop.
|
|
1632
|
-
if (node.loop_max_iters != null) {
|
|
1633
|
-
const iteration = Number(/-i(\d+)$/.exec(node.agent_id)?.[1] ?? "0");
|
|
1634
|
-
const result = await orchestrator.run(mkCtx(`${baseSpec.goal}\n\n${loopInstruction(node.loop_max_iters, iteration)}`));
|
|
1635
|
-
const pace = result.result.paceDecision;
|
|
1636
|
-
return withSignal(result, { loopContinue: pace?.action === "continue" });
|
|
1637
|
-
}
|
|
1638
|
-
// A#2 classify: run the classifier, then extract the chosen branch label (kernel prunes the rest).
|
|
1639
|
-
if (node.classify_labels && node.classify_labels.length) {
|
|
1640
|
-
const labels = node.classify_labels;
|
|
1641
|
-
const result = await orchestrator.run(mkCtx(`${baseSpec.goal}\n\n${classifyInstruction(labels)}`));
|
|
1642
|
-
const branch = extractClassifyBranch(textOf(result), labels);
|
|
1643
|
-
return branch === undefined ? result : withSignal(result, { classifyBranch: branch });
|
|
1644
|
-
}
|
|
1645
1699
|
const schema = node.output_schema;
|
|
1646
1700
|
if (!schema)
|
|
1647
1701
|
return orchestrator.run(mkCtx(baseSpec.goal));
|
|
@@ -1672,27 +1726,6 @@ export class RuntimeRunner {
|
|
|
1672
1726
|
},
|
|
1673
1727
|
};
|
|
1674
1728
|
}
|
|
1675
|
-
/**
|
|
1676
|
-
* G2: execute a deterministic reduce node — run the named reducer (built-ins overlaid with
|
|
1677
|
-
* `opts.reducers`) over its dependency outputs and return a synthetic completion. No LLM, zero
|
|
1678
|
-
* tokens. An unknown reducer or a thrown reducer fails the node (`Error` → starves dependents).
|
|
1679
|
-
*/
|
|
1680
|
-
runReduceNode(node, outputs) {
|
|
1681
|
-
const ok = (content, termination) => ({
|
|
1682
|
-
agentId: node.agent_id,
|
|
1683
|
-
result: { termination, finalMessage: { role: "assistant", content, toolCalls: [] }, turnsUsed: 0, totalTokensUsed: 0 },
|
|
1684
|
-
});
|
|
1685
|
-
const reducer = resolveReducer(node.reducer, this.opts.reducers);
|
|
1686
|
-
if (!reducer)
|
|
1687
|
-
return ok(`unknown reducer "${node.reducer}"`, "error");
|
|
1688
|
-
const inputs = (node.input_agent_ids ?? []).map(agentId => ({ agentId, output: outputs.get(agentId) ?? "" }));
|
|
1689
|
-
try {
|
|
1690
|
-
return ok(reducer(inputs), "completed");
|
|
1691
|
-
}
|
|
1692
|
-
catch (err) {
|
|
1693
|
-
return ok(`reducer "${node.reducer}" threw: ${formatToolError(err)}`, "error");
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
1729
|
/**
|
|
1697
1730
|
* W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
|
|
1698
1731
|
* through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
|
|
@@ -1715,6 +1748,10 @@ export class RuntimeRunner {
|
|
|
1715
1748
|
if (this.opts.contextPolicy) {
|
|
1716
1749
|
config.context_policy = normalizeContextPolicy(contextPolicy(this.opts.contextPolicy));
|
|
1717
1750
|
}
|
|
1751
|
+
const delegated = delegatedContextTiers(this.opts.provider?.descriptor?.().nativeContext);
|
|
1752
|
+
if (delegated) {
|
|
1753
|
+
config.context_policy = { ...config.context_policy, delegated_tiers: delegated };
|
|
1754
|
+
}
|
|
1718
1755
|
config.signal_policy = {
|
|
1719
1756
|
queue_max: signalPolicy.queueMax,
|
|
1720
1757
|
...(signalPolicy.ttlMs !== undefined ? { ttl_ms: signalPolicy.ttlMs } : {}),
|
|
@@ -1971,7 +2008,7 @@ export class RuntimeRunner {
|
|
|
1971
2008
|
}
|
|
1972
2009
|
const controller = new AbortController();
|
|
1973
2010
|
controllers.set(node.agent_id, controller);
|
|
1974
|
-
const running = this.runWorkflowNode(node, parentSessionId, orchestrator, budget,
|
|
2011
|
+
const running = this.runWorkflowNode(node, parentSessionId, orchestrator, budget, controller.signal)
|
|
1975
2012
|
.then(result => ({ kind: "node", node, result }))
|
|
1976
2013
|
.finally(() => batchState.finished.add(node.agent_id));
|
|
1977
2014
|
// A node can fail while the driver is busy feeding another completion; the race observes
|
|
@@ -2004,15 +2041,10 @@ export class RuntimeRunner {
|
|
|
2004
2041
|
if (batchState.stopping)
|
|
2005
2042
|
return { spawned: [], done: undefined };
|
|
2006
2043
|
const { node, result } = landed;
|
|
2007
|
-
//
|
|
2044
|
+
// Record this node's output so its dependents can consume it.
|
|
2008
2045
|
const outContent = result.result.finalMessage?.content;
|
|
2009
2046
|
const outText = typeof outContent === "string" ? outContent : outContent != null ? JSON.stringify(outContent) : "";
|
|
2010
2047
|
outputs.set(result.agentId, outText);
|
|
2011
|
-
// A loop iteration completes under `wf-node{N}-i{k}` but its dependents consume the STABLE
|
|
2012
|
-
// node id `wf-node{N}` — alias it so the LAST iteration's output is what dependents see.
|
|
2013
|
-
const stableId = result.agentId.replace(/-i\d+$/, "");
|
|
2014
|
-
if (stableId !== result.agentId)
|
|
2015
|
-
outputs.set(stableId, outText);
|
|
2016
2048
|
const observationStart = this.pendingObservations.length;
|
|
2017
2049
|
const completionAction = await this.commitKernelMaybeAction(runtime, this.pendingObservations, {
|
|
2018
2050
|
kind: "sub_agent_completed",
|
|
@@ -2049,17 +2081,13 @@ export class RuntimeRunner {
|
|
|
2049
2081
|
}));
|
|
2050
2082
|
}
|
|
2051
2083
|
}
|
|
2052
|
-
// Persist node completion for resume recovery
|
|
2053
|
-
//
|
|
2054
|
-
// text (post-resume dependents/reduce still see this node's output).
|
|
2084
|
+
// Persist node completion for resume recovery, with the output text so post-resume
|
|
2085
|
+
// dependents still see this node's output.
|
|
2055
2086
|
await this.opts.sessionLog.append(parentSessionId, buildWorkflowNodeCompletedEvent({
|
|
2056
2087
|
turn: runtime.turn(),
|
|
2057
2088
|
agentId: result.agentId,
|
|
2058
2089
|
status: workflowNodeStatusFromTermination(result.result.termination),
|
|
2059
2090
|
termination: result.result.termination,
|
|
2060
|
-
classifyBranch: result.result.classifyBranch,
|
|
2061
|
-
tournamentWinner: result.result.tournamentWinner,
|
|
2062
|
-
loopContinue: result.result.loopContinue,
|
|
2063
2091
|
...(result.result.finalMessage ? { output: result.result.finalMessage } : {}),
|
|
2064
2092
|
}));
|
|
2065
2093
|
return { spawned, done: findDone(obs) };
|
|
@@ -2112,94 +2140,32 @@ export class RuntimeRunner {
|
|
|
2112
2140
|
}
|
|
2113
2141
|
nextArchiveStart = compressedSeq + 1;
|
|
2114
2142
|
}
|
|
2115
|
-
// K4: a sprint renewal dropped the old history — including any earlier memory hits — so
|
|
2116
|
-
// re-run the preQueryMemory prefetch for the new sprint (live observations only).
|
|
2117
|
-
if (obs.kind === "renewed") {
|
|
2118
|
-
await this.prefetchMemoryIntoHistory(runtime, "renewal");
|
|
2119
|
-
}
|
|
2120
2143
|
}
|
|
2121
2144
|
return nextArchiveStart;
|
|
2122
2145
|
}
|
|
2123
|
-
/**
|
|
2124
|
-
*
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
const seen = new Set();
|
|
2139
|
-
const recalled = [];
|
|
2140
|
-
for (const q of queries ?? []) {
|
|
2141
|
-
if (!q.query.trim())
|
|
2146
|
+
/** The kernel's own recall for the run goal (plan task 2.1). `preQueryMemory` may rewrite it
|
|
2147
|
+
* into several scoped queries; their hits are merged, one per record. */
|
|
2148
|
+
async kernelRecallHits(goal, requestedK, phase) {
|
|
2149
|
+
const scope = this.opts.memoryScope;
|
|
2150
|
+
const queries = this.opts.preQueryMemory
|
|
2151
|
+
? await this.opts.preQueryMemory({ goal, phase }) ?? []
|
|
2152
|
+
: [{ scope, query: goal, top_k: requestedK, kinds: [] }];
|
|
2153
|
+
const seen = new Set();
|
|
2154
|
+
const hits = [];
|
|
2155
|
+
for (const query of queries) {
|
|
2156
|
+
if (!query.query.trim())
|
|
2157
|
+
continue;
|
|
2158
|
+
const limit = Math.min(query.top_k ?? requestedK, requestedK);
|
|
2159
|
+
for (const hit of (await this.opts.memoryStore.search(this.opts.agentId, { ...query, top_k: limit })).slice(0, limit)) {
|
|
2160
|
+
if (seen.has(hit.record.record_id))
|
|
2142
2161
|
continue;
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
if (seen.has(hit.record.record_id))
|
|
2146
|
-
continue;
|
|
2147
|
-
seen.add(hit.record.record_id);
|
|
2148
|
-
recalled.push(hit);
|
|
2149
|
-
lines.push(`[memory record_id=${hit.record.record_id} trust=${hit.record.provenance.trust} score=${hit.score.toFixed(3)}] ${hit.record.content}`);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
if (lines.length > 0) {
|
|
2153
|
-
await this.commitKernelApply(runtime, this.pendingObservations, {
|
|
2154
|
-
kind: "add_history_message",
|
|
2155
|
-
message: { role: "user", content: lines.join("\n") },
|
|
2156
|
-
});
|
|
2162
|
+
seen.add(hit.record.record_id);
|
|
2163
|
+
hits.push(hit);
|
|
2157
2164
|
}
|
|
2158
|
-
await this.recordHostMemoryRecall(recalled);
|
|
2159
|
-
}
|
|
2160
|
-
catch { /* errs-open */ }
|
|
2161
|
-
}
|
|
2162
|
-
async archiveSemanticPageOut(archived, action) {
|
|
2163
|
-
if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
|
|
2164
|
-
return;
|
|
2165
|
-
try {
|
|
2166
|
-
const summary = this.opts.memorySummarizer
|
|
2167
|
-
? await this.opts.memorySummarizer.summarize(archived, { action })
|
|
2168
|
-
: await summarizeForLongTermMemory(this.opts.memoryProvider ?? this.opts.provider, archived, this.opts.memorySystemPrompt);
|
|
2169
|
-
const now = Date.now();
|
|
2170
|
-
const name = `page-out-${now}`;
|
|
2171
|
-
await this.writeMemory({
|
|
2172
|
-
record_id: `${this.opts.memoryScope.tenant_id}:${this.opts.memoryScope.namespace}:project:${name}`,
|
|
2173
|
-
scope: this.opts.memoryScope, name, kind: "project", content: summary,
|
|
2174
|
-
description: `auto summary of ${action ?? "compaction"} archive`,
|
|
2175
|
-
provenance: { author: "extraction", trust: "untrusted", evidence_refs: [] },
|
|
2176
|
-
created_at: now, updated_at: now, recall_count: 0, confidence: 0.6, links: [], pinned: false,
|
|
2177
|
-
}, this.currentSessionId ?? undefined);
|
|
2178
|
-
}
|
|
2179
|
-
catch {
|
|
2180
|
-
// non-fatal
|
|
2181
2165
|
}
|
|
2166
|
+
return hits;
|
|
2182
2167
|
}
|
|
2183
2168
|
}
|
|
2184
|
-
async function summarizeForLongTermMemory(provider, archived, systemPrompt) {
|
|
2185
|
-
const transcript = archived
|
|
2186
|
-
.map(m => `${m.role}: ${m.content}`)
|
|
2187
|
-
.join("\n");
|
|
2188
|
-
const context = {
|
|
2189
|
-
systemText: [
|
|
2190
|
-
systemPrompt,
|
|
2191
|
-
"Summarize the following conversation for long-term memory. Preserve key facts, decisions, and open questions.",
|
|
2192
|
-
].filter(Boolean).join("\n\n"),
|
|
2193
|
-
turns: [{ role: "user", content: transcript, toolCalls: [] }],
|
|
2194
|
-
};
|
|
2195
|
-
let text = "";
|
|
2196
|
-
const state = provider.createRunState?.();
|
|
2197
|
-
for await (const evt of provider.stream(context, [], undefined, state)) {
|
|
2198
|
-
if (evt.type === "text_delta")
|
|
2199
|
-
text += evt.delta;
|
|
2200
|
-
}
|
|
2201
|
-
return text.trim() || transcript.slice(0, 2000);
|
|
2202
|
-
}
|
|
2203
2169
|
function isMidRun(events) {
|
|
2204
2170
|
return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
|
|
2205
2171
|
}
|