@sema-agent/core 5.56.0 → 5.57.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 +74 -0
- package/dist/agents/send-message-tool.d.ts +11 -0
- package/dist/agents/send-message-tool.js +34 -12
- package/dist/agents/team.d.ts +10 -1
- package/dist/agents/team.js +1 -0
- package/dist/brain/anthropic.js +15 -5
- package/dist/brain/circuit-breaker.js +2 -1
- package/dist/brain/degrading.js +4 -1
- package/dist/brain/failover.js +16 -1
- package/dist/brain/open-responses.js +15 -5
- package/dist/brain/openai.js +16 -5
- package/dist/brain/request-params.d.ts +30 -27
- package/dist/brain/request-params.js +1 -7
- package/dist/brain/route-adjudicator.d.ts +190 -0
- package/dist/brain/route-adjudicator.js +189 -0
- package/dist/brain/route-conformance.d.ts +55 -0
- package/dist/brain/route-conformance.js +136 -0
- package/dist/brain/routing.js +8 -3
- package/dist/core/mcp.js +4 -4
- package/dist/core/memory-engine/engine.d.ts +15 -5
- package/dist/core/memory-engine/engine.js +3 -1
- package/dist/core/permission-rule-consent.d.ts +45 -0
- package/dist/core/permission-rule-consent.js +40 -11
- package/dist/core/permission-rule-model.d.ts +110 -75
- package/dist/core/permission-rule-model.js +61 -28
- package/dist/core/runner/prepare-task.js +33 -4
- package/dist/core/runner/runtask.d.ts +4 -1
- package/dist/core/runner/runtask.js +48 -0
- package/dist/core/scheduler.d.ts +5 -0
- package/dist/core/side-query.d.ts +12 -5
- package/dist/core/types.d.ts +32 -0
- package/dist/engine/harness/agent-harness.js +26 -1
- package/dist/engine/harness/types.d.ts +5 -1
- package/dist/engine/llm/types.d.ts +65 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/internal/llm.d.ts +1 -1
- package/dist/prompts/default.d.ts +2 -2
- package/dist/prompts/default.js +2 -0
- package/dist/scenarios/scenario-registry.d.ts +5 -1
- package/dist/scenarios/scenario-registry.js +4 -2
- package/dist/tools/fs/index.js +8 -1
- package/dist/tools/scheduler-tools.js +28 -6
- package/dist/tools/web.d.ts +15 -0
- package/dist/tools/web.js +8 -2
- package/dist/tools/worktree.js +2 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +19 -1
|
@@ -61,7 +61,7 @@ export async function runScenario(opts) {
|
|
|
61
61
|
switch (profile.orchestrator) {
|
|
62
62
|
case "solo": {
|
|
63
63
|
const model = requireModel(opts.models, "default", opts.scenario);
|
|
64
|
-
const spec = { objective: opts.objective, model, signal: opts.signal };
|
|
64
|
+
const spec = { objective: opts.objective, model, signal: opts.signal, ...(opts.getApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.getApiKeyAndHeaders } : {}) };
|
|
65
65
|
return { scenario: "solo", result: await opts.runner.runTask(spec) };
|
|
66
66
|
}
|
|
67
67
|
case "team": {
|
|
@@ -81,6 +81,7 @@ export async function runScenario(opts) {
|
|
|
81
81
|
],
|
|
82
82
|
synthesizer: { model: synthModel },
|
|
83
83
|
signal: opts.signal,
|
|
84
|
+
...(opts.getApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.getApiKeyAndHeaders } : {}),
|
|
84
85
|
});
|
|
85
86
|
return { scenario: "design-review", result };
|
|
86
87
|
}
|
|
@@ -95,6 +96,7 @@ export async function runScenario(opts) {
|
|
|
95
96
|
members: codeReviewMembers(opts.reviewerCount ?? 2, teamModel),
|
|
96
97
|
synthesizer: { model: synthModel },
|
|
97
98
|
signal: opts.signal,
|
|
99
|
+
...(opts.getApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.getApiKeyAndHeaders } : {}),
|
|
98
100
|
});
|
|
99
101
|
return { scenario: "code-review", mode: "team", result };
|
|
100
102
|
}
|
|
@@ -127,7 +129,7 @@ async function runCodeReviewVerify(opts) {
|
|
|
127
129
|
result: opts.objective,
|
|
128
130
|
stats: { turns: 0, tokens: 0 },
|
|
129
131
|
};
|
|
130
|
-
const specBase = { signal: opts.signal };
|
|
132
|
+
const specBase = { signal: opts.signal, ...(opts.getApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.getApiKeyAndHeaders } : {}) };
|
|
131
133
|
const result = await verifyCompleted(opts.runner, reviewed, specBase, opts.objective, {
|
|
132
134
|
verifierModel,
|
|
133
135
|
verifierPrompt: STATIC_VERIFICATION_PROMPT,
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -79,7 +79,14 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
79
79
|
if (!readOnly && mountBackgroundTaskTools && hasBackgroundShell(env)) {
|
|
80
80
|
const sessionAxis = opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {};
|
|
81
81
|
tools.push(opts.taskRegistry
|
|
82
|
-
? createTaskOutputTool({
|
|
82
|
+
? createTaskOutputTool({
|
|
83
|
+
registry: opts.taskRegistry,
|
|
84
|
+
owner: opts.taskOwner,
|
|
85
|
+
scope: opts.taskScope,
|
|
86
|
+
...sessionAxis,
|
|
87
|
+
...(opts.oneShot !== undefined ? { oneShot: opts.oneShot } : {}),
|
|
88
|
+
notificationWired: opts.taskNotification !== undefined,
|
|
89
|
+
})
|
|
83
90
|
: createEnvTaskOutputTool(env), opts.taskRegistry
|
|
84
91
|
? createTaskStopTool({ registry: opts.taskRegistry, owner: opts.taskOwner, scope: opts.taskScope, ...sessionAxis })
|
|
85
92
|
: createEnvTaskStopTool(env));
|
|
@@ -396,10 +396,19 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
|
|
|
396
396
|
wakeupChain = next.then(() => undefined, () => undefined);
|
|
397
397
|
return next;
|
|
398
398
|
};
|
|
399
|
+
const wakeupMountNote = sched.schedulerCapabilities.supportsSessionWakeup === false
|
|
400
|
+
? `
|
|
401
|
+
|
|
402
|
+
MOUNT NOTE: this host has no resident wakeup leg, so ${SCHEDULE_WAKEUP_TOOL_NAME} refuses every scheduling call here — do not plan a self-paced loop around it (\`stop: true\` still works).`
|
|
403
|
+
: sched.schedulerCapabilities.supportsSessionLifetime === true
|
|
404
|
+
? ""
|
|
405
|
+
: `
|
|
406
|
+
|
|
407
|
+
MOUNT NOTE: this host does not vouch for session-scoped scheduling, so a wakeup scheduled here is PERSISTENT — contrary to the paragraph above, it can outlive this session instead of ending with it. End the loop explicitly with \`stop: true\` when the work is done; do not rely on the session ending to cancel it.`;
|
|
399
408
|
const scheduleWakeup = defineTool({
|
|
400
409
|
name: SCHEDULE_WAKEUP_TOOL_NAME,
|
|
401
410
|
contract: { contractId: "core.schedule_wakeup@1", implementationRevision: "1" },
|
|
402
|
-
description: SCHEDULE_WAKEUP_PROMPT
|
|
411
|
+
description: `${SCHEDULE_WAKEUP_PROMPT}${wakeupMountNote}`,
|
|
403
412
|
parameters: Type.Object({
|
|
404
413
|
delaySeconds: Type.Optional(Type.Number({
|
|
405
414
|
description: "Seconds from now to wake up. Clamped to [60, 3600] by the runtime. Required unless `stop` is true.",
|
|
@@ -433,20 +442,33 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
|
|
|
433
442
|
if (sched.schedulerCapabilities.supportsSessionWakeup === false) {
|
|
434
443
|
return errorResult(`Error (${SCHEDULE_WAKEUP_TOOL_NAME}): this environment has no resident scheduler that can honor a session wakeup — the wakeup would never fire. Wait in the foreground instead, or start the work in a self-detaching form.`);
|
|
435
444
|
}
|
|
445
|
+
const sessionReapVouched = sched.schedulerCapabilities.supportsSessionLifetime === true;
|
|
436
446
|
const clampedDelaySeconds = Math.min(3600, Math.max(60, Math.round(a.delaySeconds)));
|
|
437
447
|
const wasClamped = clampedDelaySeconds !== a.delaySeconds;
|
|
438
448
|
const pending = await listPendingWakeups();
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const r = await sched.schedule({
|
|
449
|
+
const cancelledCount = "err" in pending ? 0 : await cancelWakeups(pending.ids);
|
|
450
|
+
const cleanupIncomplete = "err" in pending || cancelledCount < pending.ids.length;
|
|
451
|
+
const r = await sched.schedule({
|
|
452
|
+
prompt: a.prompt,
|
|
453
|
+
when: { kind: "delay", delaySec: clampedDelaySeconds },
|
|
454
|
+
label: WAKEUP_LABEL,
|
|
455
|
+
mode: "session-wakeup",
|
|
456
|
+
...(sessionReapVouched ? { lifetime: "session" } : {}),
|
|
457
|
+
}, sessionCtx);
|
|
442
458
|
if (!r.ok)
|
|
443
459
|
return errorResult(`Error (${SCHEDULE_WAKEUP_TOOL_NAME}): ${r.error.message}`);
|
|
444
460
|
const scheduledFor = Date.now() + clampedDelaySeconds * 1000;
|
|
445
461
|
const hhmmss = new Date(scheduledFor).toTimeString().slice(0, 8);
|
|
446
462
|
const clampNote = wasClamped ? ` (clamped to ${clampedDelaySeconds}s from your requested value)` : "";
|
|
463
|
+
const cleanupNote = cleanupIncomplete
|
|
464
|
+
? " Warning: the previously pending wakeup(s) for this session could not all be listed or cancelled, so MORE THAN ONE wakeup may now be armed (you would be re-invoked twice). Call this tool with `stop: true` and then re-schedule to get back to a single one."
|
|
465
|
+
: "";
|
|
466
|
+
const reapNote = sessionReapVouched
|
|
467
|
+
? ""
|
|
468
|
+
: " Note: this scheduler does not vouch for session-lifetime reap, so this wakeup is scheduled as a persistent one and may outlive the session — end the loop explicitly with `stop: true` rather than relying on the session ending.";
|
|
447
469
|
return {
|
|
448
|
-
content: `Next wakeup scheduled for ${hhmmss} (in ${clampedDelaySeconds}s)${clampNote}. Nothing more to do this turn — the harness re-invokes you when the wakeup fires or a task-notification arrives
|
|
449
|
-
details: { type: "schedule-wakeup", stopped: false, scheduledFor, clampedDelaySeconds, wasClamped },
|
|
470
|
+
content: `Next wakeup scheduled for ${hhmmss} (in ${clampedDelaySeconds}s)${clampNote}. Nothing more to do this turn — the harness re-invokes you when the wakeup fires or a task-notification arrives.${reapNote}${cleanupNote}`,
|
|
471
|
+
details: { type: "schedule-wakeup", stopped: false, scheduledFor, clampedDelaySeconds, wasClamped, reason: a.reason },
|
|
450
472
|
};
|
|
451
473
|
}),
|
|
452
474
|
});
|
package/dist/tools/web.d.ts
CHANGED
|
@@ -160,6 +160,21 @@ export declare function resolveSummaryInputChars(model: Model, override?: number
|
|
|
160
160
|
export interface WebFetchSummarizerOptions {
|
|
161
161
|
/** Override the derived page-content budget (see {@link resolveSummaryInputChars}). */
|
|
162
162
|
maxContentChars?: number;
|
|
163
|
+
/**
|
|
164
|
+
* Per-model auth — MIRRORS {@link TaskSpec.getApiKeyAndHeaders} (same signature, resolved per
|
|
165
|
+
* call against the summarizer's model, the side-query seat's form). Without this seat the
|
|
166
|
+
* summarizer called the brain with `{ signal }` only, so a summarizer model on a
|
|
167
|
+
* per-model-credential route fell to the brain's construction-time credential — which the
|
|
168
|
+
* key↔URL pairing gate refuses off the deployment root. Absent ⇒ the options object is
|
|
169
|
+
* byte-identical to the pre-seat shape (no keys added).
|
|
170
|
+
*/
|
|
171
|
+
getApiKeyAndHeaders?: (model: Model) => Promise<{
|
|
172
|
+
apiKey?: string;
|
|
173
|
+
headers?: Record<string, string>;
|
|
174
|
+
} | undefined> | {
|
|
175
|
+
apiKey?: string;
|
|
176
|
+
headers?: Record<string, string>;
|
|
177
|
+
} | undefined;
|
|
163
178
|
}
|
|
164
179
|
/**
|
|
165
180
|
* 黑板 [1870] L1 / [1900] — a reference {@link WebFetchConfig.summarize} implementation: reuses a
|
package/dist/tools/web.js
CHANGED
|
@@ -725,9 +725,15 @@ export function createWebFetchSummarizer(brain, model, options = {}) {
|
|
|
725
725
|
const truncated = inputTruncated ? content.slice(0, budget) + "\n\n[Content truncated due to length...]" : content;
|
|
726
726
|
const userPrompt = `\nWeb page content:\n---\n${truncated}\n---\n\n${prompt}\n\n` + WEBFETCH_SUMMARY_GROUNDING_CLAUSE + "\n" + WEBFETCH_SUMMARY_GUIDELINES;
|
|
727
727
|
const context = { messages: [{ role: "user", content: userPrompt, timestamp: Date.now() }] };
|
|
728
|
+
const auth = await options.getApiKeyAndHeaders?.(model);
|
|
729
|
+
const callOptions = {
|
|
730
|
+
signal,
|
|
731
|
+
...(auth?.apiKey !== undefined ? { apiKey: auth.apiKey } : {}),
|
|
732
|
+
...(auth?.headers !== undefined ? { headers: auth.headers } : {}),
|
|
733
|
+
};
|
|
728
734
|
const msg = brain.complete
|
|
729
|
-
? await brain.complete(model, context,
|
|
730
|
-
: await (await Promise.resolve(brain.stream(model, context,
|
|
735
|
+
? await brain.complete(model, context, callOptions)
|
|
736
|
+
: await (await Promise.resolve(brain.stream(model, context, callOptions))).result();
|
|
731
737
|
if (msg.stopReason === "error" || msg.stopReason === "aborted") {
|
|
732
738
|
throw new Error(msg.errorMessage ?? `summarizer stopped with ${msg.stopReason}`);
|
|
733
739
|
}
|
package/dist/tools/worktree.js
CHANGED
|
@@ -248,10 +248,10 @@ export function createWorktreeTools(env, opts) {
|
|
|
248
248
|
description: "true = remove the worktree even when it has uncommitted files or new commits, permanently discarding them. Default false: a changed worktree is kept and its changes listed. Cannot be combined with `keep: true`.",
|
|
249
249
|
})),
|
|
250
250
|
action: Type.Optional(Type.Union([Type.Literal("keep"), Type.Literal("remove")], {
|
|
251
|
-
description: '"keep" leaves the worktree
|
|
251
|
+
description: '"keep" leaves the worktree on disk. "remove" takes the default removal path: an unchanged worktree is removed, a changed one is still KEPT unless `discard_changes: true`. The worktree is detached — no branch is created or deleted either way.',
|
|
252
252
|
})),
|
|
253
253
|
discard_changes: Type.Optional(Type.Boolean({
|
|
254
|
-
description: '
|
|
254
|
+
description: 'Set true to remove the worktree even when it has uncommitted files or unmerged commits — they are permanently discarded. Without it a changed worktree is KEPT (the exit still completes and the reply lists the changes plus this escape), never refused.',
|
|
255
255
|
})),
|
|
256
256
|
}),
|
|
257
257
|
effect: "write",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "design/87 L3 — frozen public export surface of src/index.ts (name -> kind). DO NOT edit by hand to silence a red test. A removed/changed entry = a SemVer-BREAKING change; bump MAJOR and update this fixture in the SAME commit (design/87 §4.2 / §5.2). Regenerate via REGEN in test/export-surface.test.ts.",
|
|
3
|
-
"count":
|
|
3
|
+
"count": 1696,
|
|
4
4
|
"exports": {
|
|
5
5
|
"A2ATaskState": "type",
|
|
6
6
|
"A2ATaskStateReversal": "type",
|
|
@@ -269,6 +269,7 @@
|
|
|
269
269
|
"EXPLORE_SYSTEM_PROMPT": "variable",
|
|
270
270
|
"EXPLORE_WHEN_TO_USE": "variable",
|
|
271
271
|
"EXPLORE_WHEN_TO_USE_LEAN": "variable",
|
|
272
|
+
"EditedRuleTextPrecheck": "type",
|
|
272
273
|
"EffectiveConfigField": "interface",
|
|
273
274
|
"EffectiveMemoryScopes": "type",
|
|
274
275
|
"EffectivePermissionRule": "interface",
|
|
@@ -729,6 +730,7 @@
|
|
|
729
730
|
"RETRYABLE_REMOTE_ERROR_CODES": "variable",
|
|
730
731
|
"ROLE_TIER_DEFAULTS": "variable",
|
|
731
732
|
"ROOT_ADOPTION_FILE": "variable",
|
|
733
|
+
"ROUTE_ADJUDICATION_CONFORMANCE_CORPUS": "variable",
|
|
732
734
|
"RULE_SYNC_DROP_CODES": "variable",
|
|
733
735
|
"RUN_WORKFLOW_TOOL_NAME": "variable",
|
|
734
736
|
"RawPrefixInputs": "interface",
|
|
@@ -801,6 +803,15 @@
|
|
|
801
803
|
"RosterEntry": "interface",
|
|
802
804
|
"RosterGcOptions": "interface",
|
|
803
805
|
"RosterStore": "interface",
|
|
806
|
+
"RouteAdjudication": "type",
|
|
807
|
+
"RouteAdjudicationVector": "interface",
|
|
808
|
+
"RouteCredential": "interface",
|
|
809
|
+
"RouteCredentialSource": "type",
|
|
810
|
+
"RoutePairingConfig": "interface",
|
|
811
|
+
"RoutePairingPosture": "type",
|
|
812
|
+
"RoutePairingStatus": "type",
|
|
813
|
+
"RouteRefusalCode": "type",
|
|
814
|
+
"RouteRefusalDetail": "interface",
|
|
804
815
|
"RoutingBrainOptions": "interface",
|
|
805
816
|
"RuleAdd": "interface",
|
|
806
817
|
"RuleAddDelta": "interface",
|
|
@@ -1142,6 +1153,7 @@
|
|
|
1142
1153
|
"acquireCcLock": "function",
|
|
1143
1154
|
"addDotsOf": "function",
|
|
1144
1155
|
"addWorktree": "function",
|
|
1156
|
+
"adjudicateModelRoute": "function",
|
|
1145
1157
|
"admitMemoryScopes": "function",
|
|
1146
1158
|
"adoptFilePermissionRuleStore": "function",
|
|
1147
1159
|
"adoptLocalDataRoot": "function",
|
|
@@ -1386,6 +1398,7 @@
|
|
|
1386
1398
|
"guardedMemoryStore": "function",
|
|
1387
1399
|
"harnessContext": "function",
|
|
1388
1400
|
"harvestFilesToPatches": "function",
|
|
1401
|
+
"hasAuthCarrier": "function",
|
|
1389
1402
|
"hasBackgroundShell": "function",
|
|
1390
1403
|
"hasDestroy": "function",
|
|
1391
1404
|
"hasScheduler": "function",
|
|
@@ -1461,6 +1474,7 @@
|
|
|
1461
1474
|
"mysqlQuery": "function",
|
|
1462
1475
|
"nextSyncBaseline": "function",
|
|
1463
1476
|
"normalizeAgentName": "function",
|
|
1477
|
+
"normalizeBaseUrl": "function",
|
|
1464
1478
|
"normalizeForExactMatch": "function",
|
|
1465
1479
|
"normalizeMemorySpec": "function",
|
|
1466
1480
|
"normalizePersistedRuleHit": "function",
|
|
@@ -1494,6 +1508,7 @@
|
|
|
1494
1508
|
"peerAxisToken": "function",
|
|
1495
1509
|
"permissionRuleSyncContract": "function",
|
|
1496
1510
|
"pgQuery": "function",
|
|
1511
|
+
"precheckEditedRuleText": "function",
|
|
1497
1512
|
"prepareCardApproval": "function",
|
|
1498
1513
|
"prepareCcImport": "function",
|
|
1499
1514
|
"prepareStarterBatch": "function",
|
|
@@ -1563,6 +1578,7 @@
|
|
|
1563
1578
|
"resolveReadFace": "function",
|
|
1564
1579
|
"resolveReasoning": "function",
|
|
1565
1580
|
"resolveReasoningProfile": "function",
|
|
1581
|
+
"resolveRouteCredential": "function",
|
|
1566
1582
|
"resolveSubagentTranscriptTier": "function",
|
|
1567
1583
|
"resolveTaskLimits": "function",
|
|
1568
1584
|
"resolveTaskModel": "function",
|
|
@@ -1573,6 +1589,8 @@
|
|
|
1573
1589
|
"retiredWorkflowNameAliases": "function",
|
|
1574
1590
|
"retryBackoffMs": "function",
|
|
1575
1591
|
"riskSeverity": "function",
|
|
1592
|
+
"routePairingStatus": "function",
|
|
1593
|
+
"routeRefusalText": "function",
|
|
1576
1594
|
"ruleAdmitsCommand": "function",
|
|
1577
1595
|
"ruleSyncVector": "function",
|
|
1578
1596
|
"runCascade": "function",
|