@sema-agent/core 7.0.1 → 7.1.0
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/CHANGELOG.md +33 -0
- package/dist/agents/repair-loop.d.ts +8 -7
- package/dist/agents/roster-store.d.ts +7 -2
- package/dist/agents/subagent.js +29 -5
- package/dist/brain/errors.d.ts +18 -0
- package/dist/brain/errors.js +3 -0
- package/dist/brain/stream-engine.js +6 -4
- package/dist/core/context-edit.d.ts +3 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hooks.d.ts +26 -6
- package/dist/core/hooks.js +8 -5
- package/dist/core/image-downsample.d.ts +4 -3
- package/dist/core/memory-engine/engine.d.ts +62 -5
- package/dist/core/memory-engine/engine.js +90 -19
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/layout.d.ts +11 -3
- package/dist/core/roles.d.ts +36 -9
- package/dist/core/roles.js +19 -6
- package/dist/core/runner/prepare-memory.js +29 -22
- package/dist/core/runner/prepare-task.d.ts +5 -3
- package/dist/core/runner/prepare-task.js +88 -47
- package/dist/core/runner/runtask.d.ts +12 -3
- package/dist/core/runner/runtask.js +32 -4
- package/dist/core/safety-axis-vocab.d.ts +1 -1
- package/dist/core/strategy-store.d.ts +4 -1
- package/dist/core/task-registry-shared.d.ts +7 -3
- package/dist/core/tool-errors.d.ts +1 -1
- package/dist/core/tool-policy.d.ts +40 -7
- package/dist/core/tool-policy.js +63 -9
- package/dist/core/types.d.ts +97 -11
- package/dist/engine/compaction/compaction.js +6 -2
- package/dist/engine/harness/agent-harness.d.ts +28 -6
- package/dist/engine/harness/agent-harness.js +34 -2
- package/dist/engine/harness/messages.js +4 -0
- package/dist/engine/harness/types.d.ts +37 -0
- package/dist/engine/harness/types.js +5 -0
- package/dist/engine/session/session.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/orchestration/builtin-workflows.d.ts +17 -9
- package/dist/orchestration/run-workflow-tool.d.ts +9 -1
- package/dist/orchestration/run-workflow-tool.js +18 -8
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-types.d.ts +1 -0
- package/dist/orchestration/workflow.d.ts +9 -1
- package/dist/orchestration/workflow.js +5 -5
- package/dist/stores/file/mailbox-store.d.ts +2 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +3 -1
|
@@ -457,37 +457,47 @@ export class MemoryEngine {
|
|
|
457
457
|
return "unpersisted";
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
|
-
let
|
|
460
|
+
let raw;
|
|
461
461
|
try {
|
|
462
|
-
|
|
462
|
+
raw = this.captureRecords.mark(sessionId, record);
|
|
463
463
|
}
|
|
464
464
|
catch {
|
|
465
|
-
|
|
465
|
+
raw = "unpersisted";
|
|
466
466
|
}
|
|
467
|
-
|
|
468
|
-
this.captureOptOutSessions.
|
|
469
|
-
|
|
467
|
+
const settle = (outcome) => {
|
|
468
|
+
if (outcome !== "unpersisted" && !this.captureOptOutSessions.has(sessionId))
|
|
469
|
+
this.captureOptOutSessions.set(sessionId, record);
|
|
470
|
+
return outcome;
|
|
471
|
+
};
|
|
472
|
+
return raw instanceof Promise ? raw.then(settle, () => "unpersisted") : settle(raw);
|
|
470
473
|
}
|
|
471
474
|
sessionCaptureOptOut(sessionId) {
|
|
472
|
-
|
|
475
|
+
const r = this.sessionCaptureOptOutOrFault(sessionId);
|
|
476
|
+
return r instanceof Promise ? r.then((s) => s.record) : r.record;
|
|
473
477
|
}
|
|
474
478
|
sessionCaptureOptOutOrFault(sessionId) {
|
|
475
479
|
const inProcess = this.captureOptOutSessions.get(sessionId);
|
|
476
480
|
if (inProcess !== undefined)
|
|
477
481
|
return { record: inProcess, fault: false };
|
|
482
|
+
let raw;
|
|
478
483
|
try {
|
|
479
|
-
|
|
480
|
-
return record !== undefined ? { record, fault: false } : { fault: false };
|
|
484
|
+
raw = this.captureRecords.read(sessionId);
|
|
481
485
|
}
|
|
482
486
|
catch {
|
|
483
487
|
return { fault: true };
|
|
484
488
|
}
|
|
489
|
+
const fold = (record) => record !== undefined ? { record, fault: false } : { fault: false };
|
|
490
|
+
return raw instanceof Promise ? raw.then(fold, () => ({ fault: true })) : fold(raw);
|
|
485
491
|
}
|
|
486
492
|
listCaptureOptOutSessions() {
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
out.
|
|
490
|
-
|
|
493
|
+
const raw = this.captureRecords.list();
|
|
494
|
+
const fold = (rows) => {
|
|
495
|
+
const out = new Set(Object.keys(rows));
|
|
496
|
+
for (const id of this.captureOptOutSessions.keys())
|
|
497
|
+
out.add(id);
|
|
498
|
+
return out;
|
|
499
|
+
};
|
|
500
|
+
return raw instanceof Promise ? raw.then(fold) : fold(raw);
|
|
491
501
|
}
|
|
492
502
|
async sweepSessionCaptureResidue(handle) {
|
|
493
503
|
const out = { swept: [], restored: [], failures: [] };
|
|
@@ -1546,7 +1556,7 @@ export class MemoryEngine {
|
|
|
1546
1556
|
warnings: [],
|
|
1547
1557
|
};
|
|
1548
1558
|
const writeScope = handle.writeScope;
|
|
1549
|
-
const captureRead = opts?.sessionId !== undefined ? this.sessionCaptureOptOutOrFault(opts.sessionId) : { fault: false };
|
|
1559
|
+
const captureRead = opts?.sessionId !== undefined ? await this.sessionCaptureOptOutOrFault(opts.sessionId) : { fault: false };
|
|
1550
1560
|
const captureOptOut = captureRead.record;
|
|
1551
1561
|
if (writeScope === null || opts?.admitNothing !== undefined || captureOptOut !== undefined || captureRead.fault) {
|
|
1552
1562
|
try {
|
|
@@ -2194,7 +2204,7 @@ export class MemoryEngine {
|
|
|
2194
2204
|
return report;
|
|
2195
2205
|
}
|
|
2196
2206
|
}
|
|
2197
|
-
const preCommitRead = lineageSessionId !== undefined ? this.sessionCaptureOptOutOrFault(lineageSessionId) : { fault: false, record: undefined };
|
|
2207
|
+
const preCommitRead = lineageSessionId !== undefined ? await this.sessionCaptureOptOutOrFault(lineageSessionId) : { fault: false, record: undefined };
|
|
2198
2208
|
if (preCommitRead.record !== undefined || preCommitRead.fault) {
|
|
2199
2209
|
if (lineageArmed) {
|
|
2200
2210
|
try {
|
|
@@ -2796,7 +2806,7 @@ export class MemoryEngine {
|
|
|
2796
2806
|
return false;
|
|
2797
2807
|
return frozenToken === undefined || held.token === frozenToken;
|
|
2798
2808
|
}
|
|
2799
|
-
consolidationEligibility(headers) {
|
|
2809
|
+
async consolidationEligibility(headers) {
|
|
2800
2810
|
let exclusions;
|
|
2801
2811
|
try {
|
|
2802
2812
|
exclusions = this.readChallengeExclusions();
|
|
@@ -2823,7 +2833,7 @@ export class MemoryEngine {
|
|
|
2823
2833
|
}
|
|
2824
2834
|
let optOutSessions;
|
|
2825
2835
|
try {
|
|
2826
|
-
optOutSessions = this.listCaptureOptOutSessions();
|
|
2836
|
+
optOutSessions = await this.listCaptureOptOutSessions();
|
|
2827
2837
|
}
|
|
2828
2838
|
catch (err) {
|
|
2829
2839
|
throw new ConsolidationRefusedError("memory.consolidation_governance_unreadable", `memory consolidation refused: the capture opt-out roster cannot be trusted (fail-closed): ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2874,7 +2884,7 @@ export class MemoryEngine {
|
|
|
2874
2884
|
}
|
|
2875
2885
|
const face = this.committedAuditFace();
|
|
2876
2886
|
const headers = await face.listHeaders([scope]);
|
|
2877
|
-
const { eligible } = this.consolidationEligibility(headers);
|
|
2887
|
+
const { eligible } = await this.consolidationEligibility(headers);
|
|
2878
2888
|
const fingerprint = opts.full === true ? undefined : row?.fingerprint;
|
|
2879
2889
|
const candidateIds = [];
|
|
2880
2890
|
for (const [id, h] of eligible) {
|
|
@@ -3040,7 +3050,7 @@ export class MemoryEngine {
|
|
|
3040
3050
|
const at = this.now();
|
|
3041
3051
|
const face = this.committedAuditFace();
|
|
3042
3052
|
const headers = await face.listHeaders([scope]);
|
|
3043
|
-
const { exclusions, superseded } = this.consolidationEligibility(headers);
|
|
3053
|
+
const { exclusions, superseded } = await this.consolidationEligibility(headers);
|
|
3044
3054
|
const headerById = new Map(headers.map((h) => [h.id, h]));
|
|
3045
3055
|
const wholeReasons = [];
|
|
3046
3056
|
for (let i = 0; i < proposal.products.length; i++) {
|
|
@@ -3921,6 +3931,67 @@ export class MemoryEngine {
|
|
|
3921
3931
|
}
|
|
3922
3932
|
return await b.listScopes();
|
|
3923
3933
|
}
|
|
3934
|
+
async sessionMemoryStatus(sessionId) {
|
|
3935
|
+
const out = {};
|
|
3936
|
+
const capture = await this.sessionCaptureOptOutOrFault(sessionId);
|
|
3937
|
+
if (capture.fault) {
|
|
3938
|
+
out.optOutSource = "fault";
|
|
3939
|
+
}
|
|
3940
|
+
else if (capture.record !== undefined) {
|
|
3941
|
+
out.captureOptedOut = true;
|
|
3942
|
+
out.optOutSource = "record";
|
|
3943
|
+
}
|
|
3944
|
+
else {
|
|
3945
|
+
out.captureOptedOut = false;
|
|
3946
|
+
}
|
|
3947
|
+
let mine;
|
|
3948
|
+
try {
|
|
3949
|
+
const committed = readLineageRecord(this.controlDir).committed;
|
|
3950
|
+
const ids = new Set();
|
|
3951
|
+
let lastAt;
|
|
3952
|
+
for (const [entryId, sessions] of Object.entries(committed)) {
|
|
3953
|
+
const c = sessions[sessionId];
|
|
3954
|
+
if (c === undefined)
|
|
3955
|
+
continue;
|
|
3956
|
+
ids.add(entryId);
|
|
3957
|
+
if (lastAt === undefined || c.lastAt > lastAt)
|
|
3958
|
+
lastAt = c.lastAt;
|
|
3959
|
+
}
|
|
3960
|
+
mine = ids;
|
|
3961
|
+
out.committedCount = ids.size;
|
|
3962
|
+
if (lastAt !== undefined)
|
|
3963
|
+
out.lastCaptureAt = lastAt;
|
|
3964
|
+
}
|
|
3965
|
+
catch {
|
|
3966
|
+
}
|
|
3967
|
+
if (mine !== undefined) {
|
|
3968
|
+
if (mine.size === 0) {
|
|
3969
|
+
out.foldedCount = 0;
|
|
3970
|
+
}
|
|
3971
|
+
else {
|
|
3972
|
+
try {
|
|
3973
|
+
const enumeration = await this.listMemoryScopes();
|
|
3974
|
+
if (enumeration.supported) {
|
|
3975
|
+
const face = this.committedAuditFace();
|
|
3976
|
+
const headers = await face.listHeaders(enumeration.scopes);
|
|
3977
|
+
const productIds = headers.filter((h) => h.distilled !== undefined).map((h) => h.id);
|
|
3978
|
+
const folded = new Set();
|
|
3979
|
+
if (productIds.length > 0) {
|
|
3980
|
+
for (const p of await face.getByIds(productIds)) {
|
|
3981
|
+
for (const row of p.frontmatter.distilled?.inputs ?? [])
|
|
3982
|
+
if (mine.has(row.id))
|
|
3983
|
+
folded.add(row.id);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
out.foldedCount = folded.size;
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
catch {
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
return out;
|
|
3994
|
+
}
|
|
3924
3995
|
committedAuditFace() {
|
|
3925
3996
|
const b = this.backend;
|
|
3926
3997
|
return b.restrictedAdoptionView?.({ audit: false }) ?? b.retrievalView?.() ?? this.backend;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, memoryRecallDisciplineSegment, entryFileHeadCarriesOrigin, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, renderAnnouncements, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, memoryConsolidationRecommendedNotice, memoryConsolidationCommittedNotice, memoryConsolidationConflictNotice, memoryConsolidationIncompleteNotice, memoryConsolidationRefusedNotice, memoryConsolidationWithheldNotice, MEMORY_CAPTURE_OPTOUT_NOTICE, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, type ConsolidationCommitReceipt, type ConsolidationReconcileReport, type ConsolidationResolveReceipt, type ConsolidationPlanSummary, type ConsolidationPlanFoldEvidence, } from "./engine.js";
|
|
1
|
+
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, memoryRecallDisciplineSegment, entryFileHeadCarriesOrigin, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, renderAnnouncements, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, memoryConsolidationRecommendedNotice, memoryConsolidationCommittedNotice, memoryConsolidationConflictNotice, memoryConsolidationIncompleteNotice, memoryConsolidationRefusedNotice, memoryConsolidationWithheldNotice, MEMORY_CAPTURE_OPTOUT_NOTICE, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, type ConsolidationCommitReceipt, type ConsolidationReconcileReport, type ConsolidationResolveReceipt, type ConsolidationPlanSummary, type ConsolidationPlanFoldEvidence, type SessionMemoryStatus, } from "./engine.js";
|
|
2
2
|
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_INDEX_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type CleanMemorySearchHit, type ExposedMemorySearchHit, type MemoryGetDetails, type MemoryIndexDetails, type MemoryIndexRow, type CleanMemoryIndexRow, type ExposedMemoryIndexRow, } from "./tools.js";
|
|
3
3
|
export { MEMORY_EXPOSURE_BANNER, MEMORY_EXPOSURE_HANDLE_TAG, MEMORY_PROVENANCE_RECALL_SENTENCE, MEMORY_PROVENANCE_SEARCH_SENTENCE, memoryExposureIndexRow, parseMemoryExposureIndexRow, } from "./provenance-wording.js";
|
|
4
4
|
export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
|
|
@@ -407,11 +407,19 @@ export declare function listSessionCaptureOptOut(controlDir: string): Record<str
|
|
|
407
407
|
* (`controlDir` + `sessionId` coordinates that outlive any live object — see the settlement handle
|
|
408
408
|
* doc in prepare-task.ts), and a live store object cannot ride that lane; its migration needs its
|
|
409
409
|
* own seam design and is registered here rather than half-built.
|
|
410
|
+
*
|
|
411
|
+
* DUAL FORM (#511 件2, additive): each leg may answer `T` OR `Promise<T>` — a SQL/remote carrier is
|
|
412
|
+
* async by nature and could not implement the sync-only trio at all. Every core consumption point
|
|
413
|
+
* awaits (or `.then`-chains where a sync public seat must stay sync-transparent), so a sync store's
|
|
414
|
+
* behavior is unchanged and an async store's Promise legs keep the exact same contract per leg:
|
|
415
|
+
* a rejected `mark` promise is held to the `"unpersisted"` refusal arm, a rejected `read` promise
|
|
416
|
+
* is the INDETERMINATE fault axis, a rejected `list` promise propagates as the enumeration failure.
|
|
417
|
+
* The File trio below keeps its synchronous signatures byte-identical.
|
|
410
418
|
*/
|
|
411
419
|
export interface SessionCaptureRecordStore {
|
|
412
|
-
mark(sessionId: string, record: SessionCaptureOptOutRecord): SessionCaptureOptOutMarkOutcome
|
|
413
|
-
read(sessionId: string): SessionCaptureOptOutRecord | undefined
|
|
414
|
-
list(): Record<string, SessionCaptureOptOutRecord
|
|
420
|
+
mark(sessionId: string, record: SessionCaptureOptOutRecord): SessionCaptureOptOutMarkOutcome | Promise<SessionCaptureOptOutMarkOutcome>;
|
|
421
|
+
read(sessionId: string): SessionCaptureOptOutRecord | undefined | Promise<SessionCaptureOptOutRecord | undefined>;
|
|
422
|
+
list(): Record<string, SessionCaptureOptOutRecord> | Promise<Record<string, SessionCaptureOptOutRecord>>;
|
|
415
423
|
}
|
|
416
424
|
/** The core-default {@link SessionCaptureRecordStore}: the file trio over `controlDir` (single-
|
|
417
425
|
* process / single-host deployments keep today's carrier byte-identical). */
|
package/dist/core/roles.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export declare function resolveModel(ref: ModelRef, models?: Record<string, Mode
|
|
|
9
9
|
export declare const DEFAULT_TIER_ORDER: readonly string[];
|
|
10
10
|
/** CC 档位词 → sema 档位(内置固定,兼容层):fable/mythos→max · opus→pro · sonnet→flash · haiku→lite。 */
|
|
11
11
|
export declare const CC_MODEL_TIER_ALIASES: Readonly<Record<string, string>>;
|
|
12
|
+
/** the DISPLAY face of a string model ref: a CC tier ALIAS (haiku/sonnet/…) resolves to its
|
|
13
|
+
* sema tier name (lite/flash/…) for labels — the alias verbatim reads as a strong claim about a
|
|
14
|
+
* specific vendor's model while the actual routing may land anywhere ("haiku" showing over a
|
|
15
|
+
* DeepSeek run, an operator field report). A non-alias string is the deployment's own model key — its
|
|
16
|
+
* honest name — and passes through unchanged. Display only; routing (`expandTiers`) is untouched. */
|
|
17
|
+
export declare function resolveModelDisplayLabel(ref: string): string;
|
|
12
18
|
/**
|
|
13
19
|
* Expand a deployment's tier bindings into the model catalog: every tier name (and every CC alias whose
|
|
14
20
|
* tier resolves) becomes a catalog key pointing at the bound Model, so the ENTIRE resolve surface
|
|
@@ -22,14 +28,13 @@ export declare const CC_MODEL_TIER_ALIASES: Readonly<Record<string, string>>;
|
|
|
22
28
|
* - an existing catalog key of the same name WINS (deployment SHADOW semantics, mirroring agents):
|
|
23
29
|
* a deployment that already ships a model literally named "pro" keeps it untouched;
|
|
24
30
|
* - a binding may itself be a catalog name or a Model object (resolved through `resolveModel`);
|
|
25
|
-
* an unknown binding name throws at construction (config error surfaces at boot, not first use)
|
|
31
|
+
* an unknown binding name throws at construction (config error surfaces at boot, not first use);
|
|
32
|
+
* - `"best"` (dynamic head-of-chain alias, CC `$L` parity) resolves to the FIRST tier in
|
|
33
|
+
* {@link DEFAULT_TIER_ORDER} that carries a DIRECT binding — "the strongest model this deployment
|
|
34
|
+
* actually bound", not a fixed tier — under the same SHADOW rule (a deployment catalog key named
|
|
35
|
+
* `best` wins); with no bound known-order tier (or no tiers at all) the key stays absent and
|
|
36
|
+
* `resolveModel("best")` refuses like any unknown ref.
|
|
26
37
|
*/
|
|
27
|
-
/** the DISPLAY face of a string model ref: a CC tier ALIAS (haiku/sonnet/…) resolves to its
|
|
28
|
-
* sema tier name (lite/flash/…) for labels — the alias verbatim reads as a strong claim about a
|
|
29
|
-
* specific vendor's model while the actual routing may land anywhere ("haiku" showing over a
|
|
30
|
-
* DeepSeek run, an operator field report). A non-alias string is the deployment's own model key — its
|
|
31
|
-
* honest name — and passes through unchanged. Display only; routing (`expandTiers`) is untouched. */
|
|
32
|
-
export declare function resolveModelDisplayLabel(ref: string): string;
|
|
33
38
|
export declare function expandTiers(models: Record<string, Model> | undefined, tiers: Record<string, ModelRef> | undefined): Record<string, Model> | undefined;
|
|
34
39
|
export interface ModelMention {
|
|
35
40
|
/** The matched model name — always one of `allowedNames` — or undefined if none was found. */
|
|
@@ -44,8 +49,27 @@ export interface ModelMention {
|
|
|
44
49
|
* pick *which configured model* to use, but can never inject an arbitrary `baseUrl`/`apiKey`/Model
|
|
45
50
|
* (see design/11). Matches the first allow-listed mention; the rest of the text is returned cleaned.
|
|
46
51
|
*
|
|
47
|
-
*
|
|
52
|
+
* **Case-INSENSITIVE, deliberately** — and deliberately UNLIKE the catalog, whose lookups
|
|
53
|
+
* ({@link resolveModel}) are exact. The two answer different questions and the asymmetry is the
|
|
54
|
+
* design, not an oversight: a chat mention is a HUMAN-typed UX surface where `@Opus` and `@opus` are
|
|
55
|
+
* the same intent, so intake is wide; a catalog key is a machine identifier a deployment declared, so
|
|
56
|
+
* lookup is exact. The join is safe because the wide half never widens the narrow one: this returns
|
|
57
|
+
* the allowlist's OWN canonical spelling (never the user's casing), so the value handed to
|
|
58
|
+
* `resolveModel` is always a key the deployment wrote. The only visible consequence is that a catalog
|
|
59
|
+
* carrying two keys differing ONLY in case is addressed by whichever the allowlist iterates last —
|
|
60
|
+
* `@x` cannot name both, and there is no spelling that disambiguates them.
|
|
61
|
+
*
|
|
62
|
+
* Typical use: `const { model, cleanedText } = parseModelMention(userMsg, Object.keys(runner.agentCatalog.models ?? {}));`
|
|
48
63
|
* then `runTask({ objective: cleanedText, model })` (omit `model` to fall back to the default role).
|
|
64
|
+
*
|
|
65
|
+
* **Feed it the EXPANDED table.** The allowlist this takes is the host's own, and on a deployment that
|
|
66
|
+
* configured `RunnerDeps.tiers` the host's `deps.models` is NOT what the Runner resolves against: the
|
|
67
|
+
* constructor runs {@link expandTiers} once and keeps the expansion PRIVATE, so tier words and CC
|
|
68
|
+
* aliases (`pro`/`flash`/`opus`/`sonnet`/`best`/…) are catalog keys inside the Runner and absent from
|
|
69
|
+
* the host's table. Passing `Object.keys(deps.models)` therefore makes a user's `@opus` silently not
|
|
70
|
+
* match — the mention falls through as ordinary text and the task quietly runs on the default role.
|
|
71
|
+
* `runner.agentCatalog.models` is the expanded table (a per-read copy); a caller that has no Runner in
|
|
72
|
+
* hand can pre-expand its own with the exported {@link expandTiers}.
|
|
49
73
|
*/
|
|
50
74
|
export declare function parseModelMention(text: string, allowedNames: Iterable<string>): ModelMention;
|
|
51
75
|
/**
|
|
@@ -55,7 +79,10 @@ export declare function parseModelMention(text: string, allowedNames: Iterable<s
|
|
|
55
79
|
* - `summarize` → `flash`(压缩本该便宜;flash 未绑沿降档链到 lite);
|
|
56
80
|
* - `advisor` → 第一档(`ultra` 起,未绑降档到 max/pro——teacher 用最强的);
|
|
57
81
|
* - `verifier` → `pro`;
|
|
58
|
-
* - `
|
|
82
|
+
* - `classifier` → `flash`(design/143 批2b:auto-mode 权限分类腿是便宜档;flash 未绑沿降档链到 lite);
|
|
83
|
+
* - `subagent`/`team`/`synthesize` 无档位默认 = 走 FALLBACK 继承主模型(LLM 可经 model 参数自选档);
|
|
84
|
+
* - `consolidate` 同样无档位默认,但它的 FALLBACK 刻意只有 `summarize` 一站(design/376,见
|
|
85
|
+
* {@link FALLBACK} 的注):未配即响亮拒,不静默骑主模型。
|
|
59
86
|
* 未配 tiers 的部署:catalog 无 TIERS_ACTIVE 标记 → 表整体 INERT,行为与从前逐字节一致
|
|
60
87
|
* (即使 catalog 恰有裸模型名 `pro`/`flash` 也不入档位路由——INERT 靠配置成立,不靠键名运气)。
|
|
61
88
|
*/
|
package/dist/core/roles.js
CHANGED
|
@@ -18,7 +18,7 @@ export const CC_MODEL_TIER_ALIASES = {
|
|
|
18
18
|
haiku: "lite",
|
|
19
19
|
};
|
|
20
20
|
export function resolveModelDisplayLabel(ref) {
|
|
21
|
-
return CC_MODEL_TIER_ALIASES[ref]
|
|
21
|
+
return Object.hasOwn(CC_MODEL_TIER_ALIASES, ref) ? CC_MODEL_TIER_ALIASES[ref] : ref;
|
|
22
22
|
}
|
|
23
23
|
const TIERS_ACTIVE = Symbol.for("sema.tiersActive");
|
|
24
24
|
export function expandTiers(models, tiers) {
|
|
@@ -27,7 +27,7 @@ export function expandTiers(models, tiers) {
|
|
|
27
27
|
const out = { ...models };
|
|
28
28
|
out[TIERS_ACTIVE] = true;
|
|
29
29
|
const bound = (name) => {
|
|
30
|
-
const b = tiers[name];
|
|
30
|
+
const b = Object.hasOwn(tiers, name) ? tiers[name] : undefined;
|
|
31
31
|
return b === undefined ? undefined : resolveModel(b, models);
|
|
32
32
|
};
|
|
33
33
|
const resolveTier = (name) => {
|
|
@@ -44,20 +44,33 @@ export function expandTiers(models, tiers) {
|
|
|
44
44
|
}
|
|
45
45
|
return undefined;
|
|
46
46
|
};
|
|
47
|
+
const shadowed = (key) => Object.hasOwn(out, key) && Boolean(out[key]);
|
|
48
|
+
const put = (key, m) => {
|
|
49
|
+
Object.defineProperty(out, key, { value: m, writable: true, enumerable: true, configurable: true });
|
|
50
|
+
};
|
|
47
51
|
const tierNames = new Set([...DEFAULT_TIER_ORDER, ...Object.keys(tiers)]);
|
|
48
52
|
for (const name of tierNames) {
|
|
49
|
-
if (
|
|
53
|
+
if (shadowed(name))
|
|
50
54
|
continue;
|
|
51
55
|
const m = resolveTier(name);
|
|
52
56
|
if (m)
|
|
53
|
-
|
|
57
|
+
put(name, m);
|
|
54
58
|
}
|
|
55
59
|
for (const [alias, tier] of Object.entries(CC_MODEL_TIER_ALIASES)) {
|
|
56
|
-
if (
|
|
60
|
+
if (shadowed(alias))
|
|
57
61
|
continue;
|
|
58
62
|
const m = resolveTier(tier);
|
|
59
63
|
if (m)
|
|
60
|
-
|
|
64
|
+
put(alias, m);
|
|
65
|
+
}
|
|
66
|
+
if (!shadowed("best")) {
|
|
67
|
+
for (const name of DEFAULT_TIER_ORDER) {
|
|
68
|
+
const m = bound(name);
|
|
69
|
+
if (m) {
|
|
70
|
+
put("best", m);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
61
74
|
}
|
|
62
75
|
return out;
|
|
63
76
|
}
|
|
@@ -174,8 +174,8 @@ export async function prepareMemory(input) {
|
|
|
174
174
|
};
|
|
175
175
|
const onEngineIncident = (err) => deps.onError?.(err, { phase: "memory", sessionId });
|
|
176
176
|
const adoptionRestricted = input.memoryPersistenceDeclared === false;
|
|
177
|
-
const armCaptureOptOut = (eng) => {
|
|
178
|
-
const standingRead = eng.sessionCaptureOptOutOrFault(sessionId);
|
|
177
|
+
const armCaptureOptOut = async (eng) => {
|
|
178
|
+
const standingRead = await eng.sessionCaptureOptOutOrFault(sessionId);
|
|
179
179
|
if (standingRead.fault) {
|
|
180
180
|
captureIndeterminate = true;
|
|
181
181
|
deps.onError?.(new Error(`the capture opt-out record store is unreadable at prepare — this session's capture state is indeterminate, and every memory commit boundary (read-side adoption included) suppresses fail-closed until the store answers.`), { phase: "memory", sessionId });
|
|
@@ -199,7 +199,7 @@ export async function prepareMemory(input) {
|
|
|
199
199
|
let anyFault = standingRead.fault;
|
|
200
200
|
let found = false;
|
|
201
201
|
for (const [ancestorId, ancestorDir] of rows) {
|
|
202
|
-
const samePlane = eng.sessionCaptureOptOutOrFault(ancestorId);
|
|
202
|
+
const samePlane = await eng.sessionCaptureOptOutOrFault(ancestorId);
|
|
203
203
|
if (samePlane.record !== undefined) {
|
|
204
204
|
found = true;
|
|
205
205
|
break;
|
|
@@ -208,7 +208,7 @@ export async function prepareMemory(input) {
|
|
|
208
208
|
anyFault = true;
|
|
209
209
|
if (ancestorDir !== undefined && ancestorDir !== eng.controlPlaneDir) {
|
|
210
210
|
try {
|
|
211
|
-
const crossPlane = deps.memoryCaptureRecordStore !== undefined ? deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
211
|
+
const crossPlane = deps.memoryCaptureRecordStore !== undefined ? await deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
212
212
|
if (crossPlane !== undefined) {
|
|
213
213
|
found = true;
|
|
214
214
|
break;
|
|
@@ -230,7 +230,7 @@ export async function prepareMemory(input) {
|
|
|
230
230
|
}
|
|
231
231
|
let forkInherited = false;
|
|
232
232
|
if (!floored && !captureDeclared && standing === undefined && input.captureForkOrigin !== undefined) {
|
|
233
|
-
const src = eng.sessionCaptureOptOutOrFault(input.captureForkOrigin);
|
|
233
|
+
const src = await eng.sessionCaptureOptOutOrFault(input.captureForkOrigin);
|
|
234
234
|
if (src.record !== undefined)
|
|
235
235
|
forkInherited = true;
|
|
236
236
|
else if (src.fault && !captureIndeterminate) {
|
|
@@ -246,7 +246,7 @@ export async function prepareMemory(input) {
|
|
|
246
246
|
throw captureOptOutDeniedError(verdict.detail);
|
|
247
247
|
}
|
|
248
248
|
if (standing === undefined) {
|
|
249
|
-
const outcome = eng.markSessionCaptureOptOut(sessionId, captureDeclared ? `declared on TaskSpec.memory.capture` : forkInherited ? `inherited capture opt-out from the forked source session` : `inherited capture opt-out floor from the spawning session`);
|
|
249
|
+
const outcome = await eng.markSessionCaptureOptOut(sessionId, captureDeclared ? `declared on TaskSpec.memory.capture` : forkInherited ? `inherited capture opt-out from the forked source session` : `inherited capture opt-out floor from the spawning session`);
|
|
250
250
|
if (outcome === "unpersisted") {
|
|
251
251
|
deliverEngineNotice(deps.onNotice, memoryCaptureOptOutUnpersistedNotice({ sessionId, ingress: "declaration" }));
|
|
252
252
|
throw captureOptOutUnpersistedError(sessionId, "declaration");
|
|
@@ -287,14 +287,14 @@ export async function prepareMemory(input) {
|
|
|
287
287
|
let injectFn;
|
|
288
288
|
let harvestBoth;
|
|
289
289
|
let toolPlanes;
|
|
290
|
-
const admitNothingOptsNow = () => {
|
|
290
|
+
const admitNothingOptsNow = async () => {
|
|
291
291
|
if (input.memoryPersistenceDeclared === false) {
|
|
292
292
|
return { admitNothing: { reason: "harvest admitted nothing: memory persistence is declared unavailable for this session (memoryPersistenceCapable: false)" } };
|
|
293
293
|
}
|
|
294
294
|
if (captureLineageIndeterminate) {
|
|
295
295
|
return { admitNothing: { reason: `harvest admitted nothing: this session's fork-lineage metadata is unreadable — whether it continues an opted-out source cannot be established, so commits are suppressed fail-closed` } };
|
|
296
296
|
}
|
|
297
|
-
const own = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
297
|
+
const own = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : await writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
298
298
|
if (own.record !== undefined) {
|
|
299
299
|
return { admitNothing: { reason: `harvest admitted nothing: this session declared memory capture opt-out (memory.capture: "off")` } };
|
|
300
300
|
}
|
|
@@ -309,11 +309,11 @@ export async function prepareMemory(input) {
|
|
|
309
309
|
if (input.captureForkOrigin !== undefined && !ancestors.has(input.captureForkOrigin))
|
|
310
310
|
ancestors.set(input.captureForkOrigin, undefined);
|
|
311
311
|
for (const [ancestorId, ancestorDir] of ancestors) {
|
|
312
|
-
const samePlane = writeEngine.sessionCaptureOptOutOrFault(ancestorId);
|
|
312
|
+
const samePlane = await writeEngine.sessionCaptureOptOutOrFault(ancestorId);
|
|
313
313
|
let crossPlane;
|
|
314
314
|
if (ancestorDir !== undefined && ancestorDir !== writeEngine.controlPlaneDir) {
|
|
315
315
|
try {
|
|
316
|
-
crossPlane = deps.memoryCaptureRecordStore !== undefined ? deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
316
|
+
crossPlane = deps.memoryCaptureRecordStore !== undefined ? await deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
317
317
|
}
|
|
318
318
|
catch {
|
|
319
319
|
return { admitNothing: { reason: `harvest admitted nothing: a spawning session's capture record store is unreadable — the floor state is indeterminate and commits are suppressed fail-closed` } };
|
|
@@ -362,7 +362,7 @@ export async function prepareMemory(input) {
|
|
|
362
362
|
const personal = createPersonalEngine(personalBackendChosen);
|
|
363
363
|
const personalEngine = personal.engine;
|
|
364
364
|
const p = planes;
|
|
365
|
-
armCaptureOptOut(p.writePlane === "personal" ? personalEngine : projectEngine);
|
|
365
|
+
await armCaptureOptOut(p.writePlane === "personal" ? personalEngine : projectEngine);
|
|
366
366
|
const projectHandle = await projectEngine.materialize(p.project, p.writePlane === "project" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
367
367
|
materializedResidue.push(...planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null));
|
|
368
368
|
const personalHandle = await personalEngine.materialize(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
@@ -393,7 +393,7 @@ export async function prepareMemory(input) {
|
|
|
393
393
|
harvestBoth = async () => {
|
|
394
394
|
const writeFirst = writeIsPersonal ? [personalEngine, personalHandle] : [projectEngine, projectHandle];
|
|
395
395
|
const readOther = writeIsPersonal ? [projectEngine, projectHandle] : [personalEngine, personalHandle];
|
|
396
|
-
const writeReport = await writeFirst[0].harvest(writeFirst[1], { ...pollutedOpts(writeFirst[0]), sessionId, ...admitNothingOptsNow() });
|
|
396
|
+
const writeReport = await writeFirst[0].harvest(writeFirst[1], { ...pollutedOpts(writeFirst[0]), sessionId, ...(await admitNothingOptsNow()) });
|
|
397
397
|
let readReport;
|
|
398
398
|
let readFailure;
|
|
399
399
|
try {
|
|
@@ -411,14 +411,14 @@ export async function prepareMemory(input) {
|
|
|
411
411
|
else if (personalOnly) {
|
|
412
412
|
const personal = createPersonalEngine(choosePersonalBackend());
|
|
413
413
|
const personalEngine = personal.engine;
|
|
414
|
-
armCaptureOptOut(personalEngine);
|
|
414
|
+
await armCaptureOptOut(personalEngine);
|
|
415
415
|
const handle = await personalEngine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
416
416
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
417
417
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
418
418
|
writeEngine = personalEngine;
|
|
419
419
|
writeHandle = handle;
|
|
420
420
|
injectFn = () => personalEngine.inject(handle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark });
|
|
421
|
-
harvestBoth = () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...admitNothingOptsNow() });
|
|
421
|
+
harvestBoth = async () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...(await admitNothingOptsNow()) });
|
|
422
422
|
toolPlanes = [
|
|
423
423
|
{
|
|
424
424
|
backend: retrievalBackend(personal.backend, adoptionRestricted || captureOptedOut || captureIndeterminate || memorySpec.writeScope === null),
|
|
@@ -439,14 +439,14 @@ export async function prepareMemory(input) {
|
|
|
439
439
|
...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)),
|
|
440
440
|
...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}),
|
|
441
441
|
});
|
|
442
|
-
armCaptureOptOut(engine);
|
|
442
|
+
await armCaptureOptOut(engine);
|
|
443
443
|
const handle = await engine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
444
444
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
445
445
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
446
446
|
writeEngine = engine;
|
|
447
447
|
writeHandle = handle;
|
|
448
448
|
injectFn = () => engine.inject(handle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark });
|
|
449
|
-
harvestBoth = () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...admitNothingOptsNow() });
|
|
449
|
+
harvestBoth = async () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...(await admitNothingOptsNow()) });
|
|
450
450
|
toolPlanes = [
|
|
451
451
|
{
|
|
452
452
|
backend: retrievalBackend(backend, adoptionRestricted || captureOptedOut || captureIndeterminate || memorySpec.writeScope === null),
|
|
@@ -457,7 +457,7 @@ export async function prepareMemory(input) {
|
|
|
457
457
|
},
|
|
458
458
|
];
|
|
459
459
|
}
|
|
460
|
-
memoryWriteGateRef.current = (w) => {
|
|
460
|
+
memoryWriteGateRef.current = async (w) => {
|
|
461
461
|
if (readOnlyEngine !== undefined && readOnlyHandle !== undefined) {
|
|
462
462
|
const ro = readOnlyEngine.gateWrite(readOnlyHandle, w.key, w.content);
|
|
463
463
|
if (!ro.ok)
|
|
@@ -477,7 +477,7 @@ export async function prepareMemory(input) {
|
|
|
477
477
|
{
|
|
478
478
|
const root = writeHandle.writableRoot;
|
|
479
479
|
if (w.key === root || w.key.startsWith(`${root}${sep}`)) {
|
|
480
|
-
const state = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
480
|
+
const state = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : await writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
481
481
|
if (state.record !== undefined || state.fault) {
|
|
482
482
|
return {
|
|
483
483
|
ok: false,
|
|
@@ -555,12 +555,19 @@ export async function prepareMemory(input) {
|
|
|
555
555
|
inject: injectFn,
|
|
556
556
|
harvest: harvestSafe,
|
|
557
557
|
captureOptOut: {
|
|
558
|
-
optedOut: () =>
|
|
558
|
+
optedOut: () => {
|
|
559
|
+
if (captureOptedOut)
|
|
560
|
+
return true;
|
|
561
|
+
const r = writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
562
|
+
return r instanceof Promise ? r.then((s) => s.record !== undefined) : r.record !== undefined;
|
|
563
|
+
},
|
|
559
564
|
indeterminate: () => {
|
|
560
565
|
if (captureOptedOut)
|
|
561
566
|
return false;
|
|
562
|
-
const
|
|
563
|
-
return
|
|
567
|
+
const r = writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
568
|
+
return r instanceof Promise
|
|
569
|
+
? r.then((s) => s.record === undefined && (captureIndeterminate || s.fault))
|
|
570
|
+
: r.record === undefined && (captureIndeterminate || r.fault);
|
|
564
571
|
},
|
|
565
572
|
flip: async (reason) => {
|
|
566
573
|
const entitlement = await resolveCaptureEntitlementFresh();
|
|
@@ -576,7 +583,7 @@ export async function prepareMemory(input) {
|
|
|
576
583
|
throw e;
|
|
577
584
|
}
|
|
578
585
|
const trimmed = reason?.trim();
|
|
579
|
-
const outcome = writeEngine.markSessionCaptureOptOut(sessionId, trimmed !== undefined && trimmed !== "" ? `host flip verb: ${trimmed.slice(0, 200)}` : "host flip verb");
|
|
586
|
+
const outcome = await writeEngine.markSessionCaptureOptOut(sessionId, trimmed !== undefined && trimmed !== "" ? `host flip verb: ${trimmed.slice(0, 200)}` : "host flip verb");
|
|
580
587
|
if (outcome === "unpersisted") {
|
|
581
588
|
deliverEngineNotice(deps.onNotice, memoryCaptureOptOutUnpersistedNotice({ sessionId, ingress: "flip-verb" }));
|
|
582
589
|
throw captureOptOutUnpersistedError(sessionId, "flip-verb");
|
|
@@ -550,8 +550,9 @@ export interface Prepared {
|
|
|
550
550
|
* the memory session mounted.
|
|
551
551
|
*/
|
|
552
552
|
captureOptOut?: {
|
|
553
|
-
|
|
554
|
-
|
|
553
|
+
/** Dual form: a sync capture store answers synchronously; a Promise-form store answers a Promise. `await` is correct on either arm. */
|
|
554
|
+
optedOut: () => boolean | Promise<boolean>;
|
|
555
|
+
indeterminate: () => boolean | Promise<boolean>;
|
|
555
556
|
flip: (reason?: string) => Promise<{
|
|
556
557
|
outcome: "created" | "existed";
|
|
557
558
|
}>;
|
|
@@ -1120,7 +1121,8 @@ export interface PrepareResume {
|
|
|
1120
1121
|
* to restore — prepare rebuilds the per-task env via `deps.executionEnvFactory` then `resumeVM(snapshotId)`
|
|
1121
1122
|
* + `postResumeInit()` (instead of running on a fresh, empty env). Threaded HERE (not via `ResumeTaskConfig`)
|
|
1122
1123
|
* so the factory stays a deployment-level `RunnerDeps` capability and never pollutes `TaskSpec` — preserving
|
|
1123
|
-
* the "untrusted caller can't inject an env" red line (remote-env.
|
|
1124
|
+
* the "untrusted caller can't inject an env" red line ({@link import("../remote-env.js").ExecutionEnvFactory}'s
|
|
1125
|
+
* own contract states it: "Lives on `RunnerDeps` (deployment-level) — NOT on `TaskSpec`"; code-ready council round-2). */
|
|
1124
1126
|
workspaceHandle?: import("../remote-env.js").WorkspaceHandle;
|
|
1125
1127
|
/**
|
|
1126
1128
|
* design/174 — the call id of the CONTENT ask whose answer an operator's approval was spent on, when
|