@sema-agent/client-core 0.89.0 → 0.91.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 +297 -0
- package/README.md +35 -24
- package/dist/adapt/arms.js +9 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
- package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
- package/dist/adapter/downstream/wiringManifestView.js +16 -0
- package/dist/adapter/runStream.d.ts +2 -0
- package/dist/adapter/runStream.js +180 -38
- package/dist/askSurvivesStandingAnswer.d.ts +10 -0
- package/dist/askSurvivesStandingAnswer.js +40 -0
- package/dist/attachmentSeatCapability.d.ts +11 -0
- package/dist/attachmentSeatCapability.js +27 -0
- package/dist/attachmentsWireCaps.d.ts +22 -2
- package/dist/attachmentsWireCaps.js +95 -22
- package/dist/compensations.js +10 -14
- package/dist/controlRouter.d.ts +9 -2
- package/dist/controlRouter.js +66 -28
- package/dist/coreValuePorts.d.ts +1 -0
- package/dist/decideFailureNote.js +4 -2
- package/dist/disableSkillsWire.d.ts +1 -0
- package/dist/disableSkillsWire.js +8 -1
- package/dist/displayUntrusted.js +153 -4
- package/dist/engineCapReader.js +12 -1
- package/dist/engineCapsCache.d.ts +3 -1
- package/dist/engineCapsCache.js +56 -6
- package/dist/engineClosedSets.d.ts +2 -0
- package/dist/engineClosedSets.js +3 -0
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +10 -0
- package/dist/engineNoticeCodes.d.ts +2 -9
- package/dist/engineNoticeCodes.js +19 -27
- package/dist/fileHistoryLegNotice.d.ts +20 -0
- package/dist/fileHistoryLegNotice.js +122 -0
- package/dist/gateOutcome.d.ts +1 -1
- package/dist/gateOutcome.js +1 -0
- package/dist/generated/engineFactTables.d.ts +13 -0
- package/dist/generated/engineFactTables.js +50 -0
- package/dist/generated/engineNoticeTables.js +0 -3
- package/dist/hitl/askGateWire.d.ts +1 -1
- package/dist/hitl/askGateWire.js +1 -1
- package/dist/hitl/crashConverged.d.ts +7 -0
- package/dist/hitl/crashConverged.js +57 -0
- package/dist/hitl/crashConvergedNotice.d.ts +4 -0
- package/dist/hitl/crashConvergedNotice.js +57 -0
- package/dist/hitl/hitlBridge.d.ts +7 -4
- package/dist/hitl/parkResolver.d.ts +7 -2
- package/dist/hitl/parkResolver.js +19 -7
- package/dist/hitl/planReviewWire.d.ts +1 -1
- package/dist/hitl/planReviewWire.js +18 -4
- package/dist/hitl/sessionPolicyWire.d.ts +5 -0
- package/dist/hitl/sessionPolicyWire.js +63 -0
- package/dist/hitl/suspendedReopen.js +2 -2
- package/dist/hitl/toolApprovalWire.d.ts +4 -1
- package/dist/hitl/toolApprovalWire.js +20 -0
- package/dist/hookMatcherVerdict.d.ts +29 -0
- package/dist/hookMatcherVerdict.js +265 -0
- package/dist/hookToolAliases.d.ts +3 -0
- package/dist/hookToolAliases.js +10 -0
- package/dist/humanTurn.d.ts +91 -0
- package/dist/humanTurn.js +253 -0
- package/dist/humanTurnRead.d.ts +105 -0
- package/dist/humanTurnRead.js +331 -0
- package/dist/index.d.ts +30 -5
- package/dist/index.js +19 -4
- package/dist/inheritEnvWire.js +1 -1
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +51 -3
- package/dist/permissionRuleIssue.d.ts +1 -1
- package/dist/permissionRuleIssue.js +22 -3
- package/dist/permissionsAllowGrantCapability.d.ts +18 -0
- package/dist/permissionsAllowGrantCapability.js +84 -0
- package/dist/permissionsDeclaration.d.ts +48 -0
- package/dist/permissionsDeclaration.js +307 -0
- package/dist/promptDelivery.d.ts +16 -5
- package/dist/promptDelivery.js +172 -32
- package/dist/providerRejection.d.ts +3 -1
- package/dist/providerRejection.js +27 -1
- package/dist/readDenyTiers.d.ts +10 -0
- package/dist/readDenyTiers.js +29 -0
- package/dist/request/taskRequest.js +27 -1
- package/dist/resumeRefusalCopy.d.ts +11 -0
- package/dist/resumeRefusalCopy.js +61 -4
- package/dist/rewindArchiveCapability.js +4 -4
- package/dist/rewindWireCaps.d.ts +2 -1
- package/dist/rewindWireCaps.js +19 -2
- package/dist/sdkRegistryTransit.d.ts +1 -0
- package/dist/sdkRegistryTransit.js +1 -0
- package/dist/seam.d.ts +2 -0
- package/dist/supportFloor.d.ts +10 -0
- package/dist/supportFloor.js +42 -0
- package/dist/taskRequestWords.d.ts +7 -0
- package/dist/taskRequestWords.js +24 -0
- package/dist/toolHistoryBadTurn.d.ts +1 -0
- package/dist/toolHistoryBadTurn.js +89 -0
- package/dist/toolHistoryMismatch.d.ts +2 -1
- package/dist/toolHistoryMismatch.js +19 -6
- package/dist/userAttachmentEnvelope.d.ts +61 -0
- package/dist/userAttachmentEnvelope.js +204 -0
- package/dist/wireFailureShape.d.ts +1 -0
- package/dist/wireFailureShape.js +4 -0
- package/dist/wireRefusalCopy.d.ts +6 -0
- package/dist/wireRefusalCopy.js +26 -3
- package/docs/INTEGRATION-CLIENTS.md +1369 -18
- package/package.json +3 -3
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { hostEnv } from './hostEnv.js';
|
|
2
2
|
import { envFlagOff } from './envFlag.js';
|
|
3
|
+
import { retiredAttachmentsKeyOf } from './taskRequestWords.js';
|
|
3
4
|
export const ATTACHMENTS_ENV = 'SEMA_ATTACHMENTS';
|
|
4
5
|
export const ATTACHMENTS_SPEC_KEYS = Object.freeze([
|
|
5
6
|
'todoReminder',
|
|
6
7
|
'todoReminderMode',
|
|
7
8
|
'changedFiles',
|
|
8
|
-
'planModeReminder',
|
|
9
9
|
'budgetUsd',
|
|
10
10
|
'backgroundTasks',
|
|
11
11
|
'toolsDelta',
|
|
@@ -17,7 +17,6 @@ export const ATTACHMENTS_DEFAULT_ON_KEYS = ['agentListing', 'skillsListing', 'ba
|
|
|
17
17
|
const OPT_IN_KEYS = [
|
|
18
18
|
'todoReminder',
|
|
19
19
|
'changedFiles',
|
|
20
|
-
'planModeReminder',
|
|
21
20
|
'budgetUsd',
|
|
22
21
|
'backgroundTasks',
|
|
23
22
|
'toolsDelta',
|
|
@@ -28,25 +27,48 @@ const DEFAULT_PAIR = ['backgroundTasks', 'toolsDelta'];
|
|
|
28
27
|
const _attachmentsKeyPins = [true, true, true, true, true, true];
|
|
29
28
|
void _attachmentsKeyPins;
|
|
30
29
|
const TODO_REMINDER_MODES = ['baseline', 'off'];
|
|
30
|
+
export class AttachmentsSpecRefused extends TypeError {
|
|
31
|
+
token;
|
|
32
|
+
why;
|
|
33
|
+
allowed;
|
|
34
|
+
constructor(message, init) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'AttachmentsSpecRefused';
|
|
37
|
+
this.token = init.token;
|
|
38
|
+
this.why = init.why;
|
|
39
|
+
this.allowed = Object.freeze([...init.allowed]);
|
|
40
|
+
if (init.key !== undefined)
|
|
41
|
+
this.key = init.key;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function legalAttachmentsTokens() {
|
|
45
|
+
return ['off', 'full', 'all', 'default', '<literalTrueKey>', '+<literalTrueKey>', ...DEFAULT_ON_ONLY_KEYS.map(k => `-${k}`), `todoReminderMode=${TODO_REMINDER_MODES.join('|')}`];
|
|
46
|
+
}
|
|
31
47
|
function refuse(token, why) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`-agentListing | -skillsListing | todoReminderMode=baseline|off`);
|
|
48
|
+
const allowed = legalAttachmentsTokens();
|
|
49
|
+
throw new AttachmentsSpecRefused(`${ATTACHMENTS_ENV}: refusing token "${token}" — ${why}. Legal tokens: ${allowed.join(' | ')}`, { token, why, allowed });
|
|
35
50
|
}
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
function deny(kind, token, why, index, key) {
|
|
52
|
+
return { ok: false, refusal: { kind, token, why, entry: index + 1, key } };
|
|
53
|
+
}
|
|
54
|
+
function judgeAttachmentsEnv(raw) {
|
|
38
55
|
if (envFlagOff(raw))
|
|
39
|
-
return allDefaultOnOff();
|
|
56
|
+
return { ok: true, spec: allDefaultOnOff() };
|
|
40
57
|
const tokens = raw.split(',').map(t => t.trim()).filter(t => t.length > 0);
|
|
58
|
+
for (const [i, token] of tokens.entries()) {
|
|
59
|
+
const named = retiredAttachmentsKeyOf(token);
|
|
60
|
+
if (named !== undefined)
|
|
61
|
+
return { ok: false, refusal: { kind: 'retired_key', token, why: named.why, entry: i + 1, key: named.key, retired: named } };
|
|
62
|
+
}
|
|
41
63
|
if (tokens.length === 0)
|
|
42
|
-
return { backgroundTasks: true, toolsDelta: true };
|
|
64
|
+
return { ok: true, spec: { backgroundTasks: true, toolsDelta: true } };
|
|
43
65
|
if (tokens.length === 1 && (tokens[0] === 'full' || tokens[0] === 'all'))
|
|
44
|
-
return allLiteralTrue();
|
|
66
|
+
return { ok: true, spec: allLiteralTrue() };
|
|
45
67
|
if (tokens.length === 1 && !isKnownToken(tokens[0])) {
|
|
46
|
-
return { backgroundTasks: true, toolsDelta: true };
|
|
68
|
+
return { ok: true, spec: { backgroundTasks: true, toolsDelta: true } };
|
|
47
69
|
}
|
|
48
70
|
const spec = {};
|
|
49
|
-
for (const token of tokens) {
|
|
71
|
+
for (const [i, token] of tokens.entries()) {
|
|
50
72
|
if (token === 'full' || token === 'all') {
|
|
51
73
|
Object.assign(spec, allLiteralTrue());
|
|
52
74
|
continue;
|
|
@@ -59,11 +81,11 @@ export function attachmentsForRequest(env = hostEnv()) {
|
|
|
59
81
|
if (token.startsWith('-')) {
|
|
60
82
|
const key = canonicalKey(token.slice(1));
|
|
61
83
|
if (key === undefined)
|
|
62
|
-
|
|
84
|
+
return deny('unknown_key', token, 'unknown attachments key', i, undefined);
|
|
63
85
|
if (!isDefaultOnKey(key)) {
|
|
64
|
-
|
|
86
|
+
return deny('minus_on_literal_true_key', token, `"${key}" belongs to the literal-true family whose OFF is "delete the key", not "send false" ` +
|
|
65
87
|
'(core wire contract, board [479] ask-2) — simply do not turn it on. ' +
|
|
66
|
-
`Only the core DEFAULT-ON keys (${ATTACHMENTS_DEFAULT_ON_KEYS.join(' / ')}) accept an explicit false
|
|
88
|
+
`Only the core DEFAULT-ON keys (${ATTACHMENTS_DEFAULT_ON_KEYS.join(' / ')}) accept an explicit false`, i, key);
|
|
67
89
|
}
|
|
68
90
|
spec[key] = false;
|
|
69
91
|
continue;
|
|
@@ -71,25 +93,76 @@ export function attachmentsForRequest(env = hostEnv()) {
|
|
|
71
93
|
const [lhs, rhs] = splitAssign(token);
|
|
72
94
|
if (rhs !== undefined) {
|
|
73
95
|
if (canonicalKey(lhs) !== 'todoReminderMode')
|
|
74
|
-
|
|
96
|
+
return deny('value_form_on_other_key', token, 'only `todoReminderMode` takes a `key=value` form', i, canonicalKey(lhs));
|
|
75
97
|
const mode = TODO_REMINDER_MODES.find(v => v === rhs);
|
|
76
98
|
if (mode === undefined)
|
|
77
|
-
|
|
99
|
+
return deny('todo_mode_bad_value', token, `todoReminderMode must be one of ${TODO_REMINDER_MODES.join('|')}`, i, 'todoReminderMode');
|
|
78
100
|
spec.todoReminderMode = mode;
|
|
79
101
|
continue;
|
|
80
102
|
}
|
|
81
103
|
const key = canonicalKey(lhs.startsWith('+') ? lhs.slice(1) : lhs);
|
|
82
104
|
if (key === undefined)
|
|
83
|
-
|
|
105
|
+
return deny('unknown_key', token, 'unknown attachments key', i, undefined);
|
|
84
106
|
if (isDefaultOnOnlyKey(key)) {
|
|
85
|
-
|
|
86
|
-
`use "-${key}" to turn it OFF`);
|
|
107
|
+
return deny('plus_on_default_on_key', token, `"${key}" is core DEFAULT-ON — turning it "on" is a no-op that would send a redundant true; ` + `use "-${key}" to turn it OFF`, i, key);
|
|
87
108
|
}
|
|
88
109
|
if (key === 'todoReminderMode')
|
|
89
|
-
|
|
110
|
+
return deny('todo_mode_needs_value', token, 'todoReminderMode needs a value: `todoReminderMode=baseline|off`', i, key);
|
|
90
111
|
spec[key] = true;
|
|
91
112
|
}
|
|
92
|
-
return spec;
|
|
113
|
+
return { ok: true, spec };
|
|
114
|
+
}
|
|
115
|
+
function envRawOf(env) {
|
|
116
|
+
return (env[ATTACHMENTS_ENV] ?? '').trim().toLowerCase();
|
|
117
|
+
}
|
|
118
|
+
export function attachmentsForRequest(env = hostEnv()) {
|
|
119
|
+
const judged = judgeAttachmentsEnv(envRawOf(env));
|
|
120
|
+
if (!judged.ok)
|
|
121
|
+
return refuse(judged.refusal.token, judged.refusal.why);
|
|
122
|
+
return judged.spec;
|
|
123
|
+
}
|
|
124
|
+
function attachmentsWayOut() {
|
|
125
|
+
const on = OPT_IN_KEYS.join(', ');
|
|
126
|
+
const off = ATTACHMENTS_DEFAULT_ON_KEYS.join(', ');
|
|
127
|
+
return (`Accepted writing is either the single word off on its own (every attachment off; it cannot be combined with other entries), or a comma-separated list of: ` +
|
|
128
|
+
`full or all (every opt-in attachment on), default, the name of an attachment to turn on (${on}), a leading minus to turn off one of ${off}, and todoReminderMode=${TODO_REMINDER_MODES.join(' or todoReminderMode=')}. ` +
|
|
129
|
+
`To drop the setting altogether, unset ${ATTACHMENTS_ENV}; the defaults then apply.`);
|
|
130
|
+
}
|
|
131
|
+
function attachmentsRefusalSentence(r) {
|
|
132
|
+
const head = `The attachments setting (${ATTACHMENTS_ENV}) cannot be used as it is set: entry ${r.entry} of the list`;
|
|
133
|
+
const key = r.key ?? '';
|
|
134
|
+
switch (r.kind) {
|
|
135
|
+
case 'retired_key':
|
|
136
|
+
return `${head} names "${key}", a key that no longer exists — ${r.retired?.because ?? 'it was retired'}, and a request carrying that key is refused by the server. Remove that entry${r.retired === undefined ? '' : `; ${r.retired.remedy}`}. ${attachmentsWayOut()}`;
|
|
137
|
+
case 'unknown_key':
|
|
138
|
+
return `${head} is not an attachment this version knows. ${attachmentsWayOut()}`;
|
|
139
|
+
case 'minus_on_literal_true_key':
|
|
140
|
+
return key === 'todoReminderMode'
|
|
141
|
+
? `${head} puts a minus on todoReminderMode, which is a setting with a value rather than something to turn off — write todoReminderMode=${TODO_REMINDER_MODES.join(' or todoReminderMode=')}, or leave it out of the list. A minus only works on ${ATTACHMENTS_DEFAULT_ON_KEYS.join(', ')}, which are on by default. ${attachmentsWayOut()}`
|
|
142
|
+
: `${head} puts a minus on ${key}, and a minus cannot switch that attachment off — it is on only when it is named, or when full, all or default switches it on. A minus only works on ${ATTACHMENTS_DEFAULT_ON_KEYS.join(', ')}, which are on by default. To leave ${key} out, do not name it, and if full, all or default is what switches it on, list the attachments you want one by one instead. ${attachmentsWayOut()}`;
|
|
143
|
+
case 'plus_on_default_on_key':
|
|
144
|
+
return `${head} turns on ${key}, which is already on by default. To turn it off write -${key}; otherwise leave it out of the list. ${attachmentsWayOut()}`;
|
|
145
|
+
case 'value_form_on_other_key':
|
|
146
|
+
return `${head} uses a name=value form, and only todoReminderMode takes one. ${attachmentsWayOut()}`;
|
|
147
|
+
case 'todo_mode_bad_value':
|
|
148
|
+
return `${head} gives todoReminderMode a value it does not accept; the values are ${TODO_REMINDER_MODES.join(' and ')}. ${attachmentsWayOut()}`;
|
|
149
|
+
case 'todo_mode_needs_value':
|
|
150
|
+
return `${head} names todoReminderMode without a value; write todoReminderMode=${TODO_REMINDER_MODES.join(' or todoReminderMode=')}. ${attachmentsWayOut()}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function attachmentsUnreadableSentence() {
|
|
154
|
+
return `The attachments setting (${ATTACHMENTS_ENV}) could not be read as text, so it cannot be used as it is set. Set it to a plain comma-separated list, or unset it. ${attachmentsWayOut()}`;
|
|
155
|
+
}
|
|
156
|
+
export function attachmentsSubmitVerdict(env = hostEnv()) {
|
|
157
|
+
try {
|
|
158
|
+
const judged = judgeAttachmentsEnv(envRawOf(env));
|
|
159
|
+
if (judged.ok)
|
|
160
|
+
return { ok: true, attachments: judged.spec };
|
|
161
|
+
return { ok: false, sentence: attachmentsRefusalSentence(judged.refusal), refusedKey: judged.refusal.key };
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return { ok: false, sentence: attachmentsUnreadableSentence(), refusedKey: undefined };
|
|
165
|
+
}
|
|
93
166
|
}
|
|
94
167
|
function allDefaultOnOff() {
|
|
95
168
|
const out = {};
|
package/dist/compensations.js
CHANGED
|
@@ -7,8 +7,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
7
7
|
libraryHalf: 'adapt.ts bindTaskCard()',
|
|
8
8
|
chromeArm: null,
|
|
9
9
|
hostHalf: null,
|
|
10
|
-
retireOn: '
|
|
11
|
-
'FIFO 回落臂可删 —— 但只在**确认不再支持老引擎**时删,见 bindTaskCard 的 old-engine fallback 注释',
|
|
10
|
+
retireOn: 'RL-31',
|
|
12
11
|
},
|
|
13
12
|
{
|
|
14
13
|
id: 'T34',
|
|
@@ -26,7 +25,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
26
25
|
libraryHalf: 'adapt.ts fireSubagentStart()(类型恒记 + 一生一次去重 + if-started 门)',
|
|
27
26
|
chromeArm: 'subagent_lifecycle',
|
|
28
27
|
hostHalf: '真执行用户 hook 进程(浏览器宿主根本不能执行 ⇒ 这一半必须留端)',
|
|
29
|
-
retireOn: '
|
|
28
|
+
retireOn: 'RL-32',
|
|
30
29
|
},
|
|
31
30
|
{
|
|
32
31
|
id: 'T36',
|
|
@@ -35,7 +34,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
35
34
|
libraryHalf: 'adapt.ts settlePanelTasks 的四个调用点(tool_end 关卡 / result / turn 末 / abort)',
|
|
36
35
|
chromeArm: 'inline_task_stats',
|
|
37
36
|
hostHalf: null,
|
|
38
|
-
retireOn: '
|
|
37
|
+
retireOn: 'RL-33',
|
|
39
38
|
},
|
|
40
39
|
{
|
|
41
40
|
id: 'T44',
|
|
@@ -53,7 +52,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
53
52
|
libraryHalf: 'adapt.ts task_progress 臂的 workflowRunId / wa-id / workflow-card-parent 三级门',
|
|
54
53
|
chromeArm: null,
|
|
55
54
|
hostHalf: null,
|
|
56
|
-
retireOn: '
|
|
55
|
+
retireOn: 'RL-34',
|
|
57
56
|
},
|
|
58
57
|
{
|
|
59
58
|
id: 'T48',
|
|
@@ -63,7 +62,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
63
62
|
chromeArm: null,
|
|
64
63
|
hostHalf: 'footer 行过滤 isFooterTaskRow/filterFooterTaskRows —— 含「丢弃所有无 parentId 顶层行」' +
|
|
65
64
|
'这条**端专属决策**(主 run 头部行由端自己合成渲染),留 TUI',
|
|
66
|
-
retireOn: '
|
|
65
|
+
retireOn: 'RL-35',
|
|
67
66
|
},
|
|
68
67
|
{
|
|
69
68
|
id: 'T1',
|
|
@@ -83,8 +82,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
83
82
|
libraryHalf: 'notifications.ts taskNotificationDedupKey + taskNotificationDedupKeyFromWire(裸 wire 入口)',
|
|
84
83
|
chromeArm: null,
|
|
85
84
|
hostHalf: null,
|
|
86
|
-
retireOn: '
|
|
87
|
-
'删之前要先有一批「同一完成在两条通道上 completionId 相同」的实测(否则换成了一把更差的键)',
|
|
85
|
+
retireOn: 'RL-36',
|
|
88
86
|
},
|
|
89
87
|
{
|
|
90
88
|
id: 'T3',
|
|
@@ -95,8 +93,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
95
93
|
'adapt.ts importLedgerFromTranscript(消费口)',
|
|
96
94
|
chromeArm: null,
|
|
97
95
|
hostHalf: null,
|
|
98
|
-
retireOn: '
|
|
99
|
-
'completionId。所以退的是「XML 反解」这一段,不是整条回植',
|
|
96
|
+
retireOn: 'RL-37',
|
|
100
97
|
},
|
|
101
98
|
{
|
|
102
99
|
id: 'T25',
|
|
@@ -106,7 +103,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
106
103
|
libraryHalf: 'toolResult.ts 终态轮询卡臂的预 seed + notifications.markEngineWorkflowNotified',
|
|
107
104
|
chromeArm: null,
|
|
108
105
|
hostHalf: null,
|
|
109
|
-
retireOn: '
|
|
106
|
+
retireOn: 'RL-38',
|
|
110
107
|
},
|
|
111
108
|
{
|
|
112
109
|
id: 'T53',
|
|
@@ -115,8 +112,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
115
112
|
libraryHalf: 'adapt.ts task_notification / workflow_complete 两臂的四道门(逐道说明见本行上方注释)',
|
|
116
113
|
chromeArm: null,
|
|
117
114
|
hostHalf: null,
|
|
118
|
-
retireOn: '
|
|
119
|
-
'🔴 判据不是「四变一」而是逐道过一遍上面那四行 —— 把 ④ 当成去重删掉会让队列里的旧条目重新上屏',
|
|
115
|
+
retireOn: 'RL-39',
|
|
120
116
|
},
|
|
121
117
|
{
|
|
122
118
|
id: 'T49',
|
|
@@ -125,7 +121,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
125
121
|
libraryHalf: 'adapt.ts task_progress 臂 registerSubagentAlias(explicitParent, taskId)',
|
|
126
122
|
chromeArm: null,
|
|
127
123
|
hostHalf: null,
|
|
128
|
-
retireOn: '
|
|
124
|
+
retireOn: 'RL-40',
|
|
129
125
|
},
|
|
130
126
|
];
|
|
131
127
|
export function compensationSplitViolations() {
|
package/dist/controlRouter.d.ts
CHANGED
|
@@ -21,7 +21,12 @@ export type ControlSafetyCode = 'not_running' | 'invalid_content' | 'queue_full'
|
|
|
21
21
|
export declare class ControlSafetyError extends Error {
|
|
22
22
|
readonly code: ControlSafetyCode;
|
|
23
23
|
readonly cause?: unknown | undefined;
|
|
24
|
-
|
|
24
|
+
readonly advice?: string;
|
|
25
|
+
readonly status?: number;
|
|
26
|
+
constructor(message: string, code: ControlSafetyCode, cause?: unknown | undefined, detail?: {
|
|
27
|
+
readonly advice?: string;
|
|
28
|
+
readonly status?: number;
|
|
29
|
+
});
|
|
25
30
|
}
|
|
26
31
|
export type QueuePriority = 'now' | 'next' | 'later';
|
|
27
32
|
export interface QueuedCommand {
|
|
@@ -72,8 +77,10 @@ export interface ErrCodes {
|
|
|
72
77
|
}
|
|
73
78
|
export declare function errCodes(e: unknown): ErrCodes;
|
|
74
79
|
export declare function steeringStatusIs4xx(e: object): boolean;
|
|
75
|
-
export interface
|
|
80
|
+
export interface SteerRefusalAdvice {
|
|
76
81
|
safety: ControlSafetyCode;
|
|
77
82
|
advice: string;
|
|
78
83
|
}
|
|
84
|
+
export type SteeringRefusedBeforeEngine = SteerRefusalAdvice;
|
|
79
85
|
export declare function steeringRefusedBeforeEngine(e: object): SteeringRefusedBeforeEngine | undefined;
|
|
86
|
+
export declare function steerRefusalAdviceOf(e: object): SteerRefusalAdvice | undefined;
|
package/dist/controlRouter.js
CHANGED
|
@@ -3,11 +3,17 @@ import { uuid } from './adapter/types.js';
|
|
|
3
3
|
export class ControlSafetyError extends Error {
|
|
4
4
|
code;
|
|
5
5
|
cause;
|
|
6
|
-
constructor(message, code, cause) {
|
|
6
|
+
constructor(message, code, cause, detail) {
|
|
7
7
|
super(message);
|
|
8
8
|
this.code = code;
|
|
9
9
|
this.cause = cause;
|
|
10
10
|
this.name = 'ControlSafetyError';
|
|
11
|
+
const advice = detail?.advice;
|
|
12
|
+
const status = detail?.status;
|
|
13
|
+
if (typeof advice === 'string' && advice.length > 0)
|
|
14
|
+
this.advice = advice;
|
|
15
|
+
if (typeof status === 'number')
|
|
16
|
+
this.status = status;
|
|
11
17
|
}
|
|
12
18
|
}
|
|
13
19
|
const BAND_RANK = { now: 0, next: 1, later: 2 };
|
|
@@ -55,39 +61,38 @@ export class ControlRouter {
|
|
|
55
61
|
}
|
|
56
62
|
async steer(text, mode, opts) {
|
|
57
63
|
if (this.taskId === null) {
|
|
58
|
-
throw
|
|
64
|
+
throw controlSafetyRefusal('not_running', 'no run in flight to steer', 'submit a turn first');
|
|
59
65
|
}
|
|
60
66
|
const body = mode !== undefined ? { text, mode } : { text };
|
|
61
67
|
try {
|
|
62
68
|
return await this.client.runs.steer(this.taskId, body, opts?.signal !== undefined ? { signal: opts.signal } : undefined);
|
|
63
69
|
}
|
|
64
70
|
catch (e) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
throw new ControlSafetyError(`${wireCode} — ${steeringAdviceOf(e, safety) ?? 'steering refused'}`, safety, e);
|
|
71
|
+
if (typeof e === 'object' && e !== null) {
|
|
72
|
+
const refusal = steerRefusalAdviceOf(e);
|
|
73
|
+
if (refusal !== undefined)
|
|
74
|
+
throw controlSafetyRefusal(refusal.safety, steerRefusalWireLabel(e), refusal.advice, e);
|
|
70
75
|
}
|
|
71
76
|
throw e;
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
async cancel(opts) {
|
|
75
80
|
if (this.taskId === null) {
|
|
76
|
-
throw
|
|
81
|
+
throw controlSafetyRefusal('not_found', 'no run in flight to cancel');
|
|
77
82
|
}
|
|
78
83
|
try {
|
|
79
84
|
return await this.client.runs.cancel(this.taskId, opts?.signal !== undefined ? { signal: opts.signal } : undefined);
|
|
80
85
|
}
|
|
81
86
|
catch (e) {
|
|
82
87
|
if (cancelConflictFromError(e)?.kind === 'run_not_running') {
|
|
83
|
-
throw
|
|
88
|
+
throw controlSafetyRefusal('cancel_run_not_running', 'runs.cancel 409 conflict.run_not_running', cancelConflictContent({ kind: 'run_not_running' }), e);
|
|
84
89
|
}
|
|
85
90
|
if (isCancelSuspendedConflict(e)) {
|
|
86
|
-
throw
|
|
87
|
-
'check what that decision did to the run before retrying the cancel',
|
|
91
|
+
throw controlSafetyRefusal('cancel_lost_race', 'runs.cancel 409', 'the pending gate was decided or expired concurrently (lost CAS race); ' +
|
|
92
|
+
'check what that decision did to the run before retrying the cancel', e);
|
|
88
93
|
}
|
|
89
94
|
if (isNotFound(e)) {
|
|
90
|
-
throw
|
|
95
|
+
throw controlSafetyRefusal('not_found', 'runs.cancel 404 — non-owner / unknown run (no existence leak)', undefined, e);
|
|
91
96
|
}
|
|
92
97
|
throw e;
|
|
93
98
|
}
|
|
@@ -149,17 +154,17 @@ const STEERING_NAME_TO_SAFETY = new Map([
|
|
|
149
154
|
['SteeringLedgerUnavailableError', 'ledger_unavailable'],
|
|
150
155
|
['SteeringError', 'steering_other'],
|
|
151
156
|
]);
|
|
152
|
-
const STEERING_ADVICE =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
const STEERING_ADVICE = {
|
|
158
|
+
not_running: 'the run is suspended/terminal/on another replica — resolve its gate via decide, do not steer',
|
|
159
|
+
invalid_content: 'the steer text carried a control-plane escape; surface it, do not strip-and-retry',
|
|
160
|
+
queue_full: 'the parked steering queue on this checkpoint is full (core fails loud rather than evicting an accepted instruction) — wait for it to drain, do not resend',
|
|
161
|
+
duplicate_input_id: 'this Idempotency-Key is already parked with DIFFERENT steering content — reissue with a fresh key, resending verbatim will never succeed',
|
|
162
|
+
ambiguous_target: 'more than one live target matches — address it uniquely (sub-agent: parentToolCallId; workflow: a more specific label); retrying verbatim yields the same result',
|
|
163
|
+
still_running: 'the target is STILL RUNNING — steer it instead of resuming, or wait for it to settle (this is the exact opposite of not_running: never collapse the two)',
|
|
164
|
+
blocked_by_hook: "this deployment's userPromptSubmit gate refused the input (block, timeout and crash all report this one code, fail-closed) — the input was NOT accepted: no human_input frame, the inputId is not on the ledger, so editing the text and sending again is a normal retry, not a duplicate",
|
|
165
|
+
ledger_unavailable: 'the server could not record this steer in its user-input ledger and refused it before anything reached the engine — the input was NOT delivered; resend the same text later (the same Idempotency-Key is fine)',
|
|
166
|
+
steering_other: 'an unrecognized steering.* refusal (open set) — the steer did NOT land; log the errorCode verbatim, do not guess its meaning',
|
|
167
|
+
};
|
|
163
168
|
const STEERING_OTHER_UNKNOWN_ADVICE = 'an unrecognized steering.* refusal (open set) — treat delivery as unknown (do not resend as if it had certainly been dropped); log the errorCode verbatim, do not guess its meaning';
|
|
164
169
|
export function steeringStatusIs4xx(e) {
|
|
165
170
|
let status;
|
|
@@ -172,9 +177,9 @@ export function steeringStatusIs4xx(e) {
|
|
|
172
177
|
return status !== undefined && status >= 400 && status < 500;
|
|
173
178
|
}
|
|
174
179
|
function steeringAdviceOf(e, safety) {
|
|
175
|
-
if (safety === 'steering_other' && !
|
|
180
|
+
if (safety === 'steering_other' && !steeringStatusIs4xx(e))
|
|
176
181
|
return STEERING_OTHER_UNKNOWN_ADVICE;
|
|
177
|
-
return STEERING_ADVICE
|
|
182
|
+
return STEERING_ADVICE[safety];
|
|
178
183
|
}
|
|
179
184
|
const STEERING_REFUSED_BEFORE_ENGINE = new Map([['ledger_unavailable', 503]]);
|
|
180
185
|
export function steeringRefusedBeforeEngine(e) {
|
|
@@ -185,8 +190,7 @@ export function steeringRefusedBeforeEngine(e) {
|
|
|
185
190
|
const safety = steeringVerdict(e);
|
|
186
191
|
if (safety === undefined || STEERING_REFUSED_BEFORE_ENGINE.get(safety) !== status)
|
|
187
192
|
return undefined;
|
|
188
|
-
|
|
189
|
-
return advice === undefined ? undefined : { safety, advice };
|
|
193
|
+
return { safety, advice: STEERING_ADVICE[safety] };
|
|
190
194
|
}
|
|
191
195
|
catch {
|
|
192
196
|
return undefined;
|
|
@@ -195,7 +199,7 @@ export function steeringRefusedBeforeEngine(e) {
|
|
|
195
199
|
function steeringSafetyOf(e, verdict) {
|
|
196
200
|
if (!STEERING_REFUSED_BEFORE_ENGINE.has(verdict))
|
|
197
201
|
return verdict;
|
|
198
|
-
const refused =
|
|
202
|
+
const refused = steeringRefusedBeforeEngine(e);
|
|
199
203
|
return refused !== undefined && refused.safety === verdict ? verdict : 'steering_other';
|
|
200
204
|
}
|
|
201
205
|
function steeringVerdict(e) {
|
|
@@ -207,6 +211,40 @@ function steeringVerdict(e) {
|
|
|
207
211
|
return STEERING_NAME_TO_SAFETY.get(name);
|
|
208
212
|
return undefined;
|
|
209
213
|
}
|
|
214
|
+
export function steerRefusalAdviceOf(e) {
|
|
215
|
+
try {
|
|
216
|
+
const verdict = steeringVerdict(e);
|
|
217
|
+
if (verdict === undefined)
|
|
218
|
+
return undefined;
|
|
219
|
+
const safety = steeringSafetyOf(e, verdict);
|
|
220
|
+
return { safety, advice: steeringAdviceOf(e, safety) };
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function steerRefusalWireLabel(e) {
|
|
227
|
+
try {
|
|
228
|
+
const { code, name } = errCodes(e);
|
|
229
|
+
return code ?? name ?? 'steering.<unknown>';
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return 'steering.<unknown>';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function statusOfThrown(e) {
|
|
236
|
+
try {
|
|
237
|
+
return errCodes(e).status;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function controlSafetyRefusal(code, head, advice, cause) {
|
|
244
|
+
const status = statusOfThrown(cause);
|
|
245
|
+
const detail = { ...(advice !== undefined ? { advice } : {}), ...(status !== undefined ? { status } : {}) };
|
|
246
|
+
return new ControlSafetyError(advice === undefined ? head : `${head} — ${advice}`, code, cause, detail);
|
|
247
|
+
}
|
|
210
248
|
function isCancelSuspendedConflict(e) {
|
|
211
249
|
const { name, status } = errCodes(e);
|
|
212
250
|
return name === 'ConflictError' || status === 409;
|
package/dist/coreValuePorts.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface AllowDenyPolicyOptionsView {
|
|
|
16
16
|
deny?: string[];
|
|
17
17
|
onInvalidName?: 'throw' | 'skip';
|
|
18
18
|
onInvalidNameIssue?: (issue: AllowDenyInvalidNameIssueView) => void;
|
|
19
|
+
scope?: 'grant' | 'narrowing';
|
|
19
20
|
}
|
|
20
21
|
export interface AllowDenyInvalidNameIssueView {
|
|
21
22
|
entry: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { corruptStoredRowContent, corruptStoredRowWhere } from './wireErrorTriage.js';
|
|
2
|
-
import { parkedResumeStartupContent, parkedResumeStartupFromError, resumePolicyBlockContent, resumePolicyBlockFromError, } from './resumeRefusalCopy.js';
|
|
2
|
+
import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError, parkedResumeStartupContent, parkedResumeStartupFromError, resumePolicyBlockContent, resumePolicyBlockFromError, } from './resumeRefusalCopy.js';
|
|
3
3
|
import { decideParkMovedContent, decideParkMovedFromError } from './decideReceipt.js';
|
|
4
4
|
import { settingsPermissionBucketRefusalContent, settingsPermissionBucketRefusalFromError } from './wireRefusalCopy.js';
|
|
5
5
|
export function decideFailureNote(err, decision) {
|
|
@@ -8,9 +8,11 @@ export function decideFailureNote(err, decision) {
|
|
|
8
8
|
const startup = parkedResumeStartupFromError(err, decision);
|
|
9
9
|
const moved = decideParkMovedFromError(err);
|
|
10
10
|
const bucket = settingsPermissionBucketRefusalFromError(err);
|
|
11
|
+
const unsupported = checkpointUnsupportedVersionFromError(err);
|
|
11
12
|
return ((where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`) +
|
|
12
13
|
(fold === null ? '' : ` — ${resumePolicyBlockContent(fold)}`) +
|
|
13
14
|
(bucket === null ? '' : ` — ${settingsPermissionBucketRefusalContent(bucket)}`) +
|
|
14
15
|
(startup === null ? '' : ` — ${parkedResumeStartupContent(startup)}`) +
|
|
15
|
-
(moved === null ? '' : ` — ${decideParkMovedContent(moved)}`)
|
|
16
|
+
(moved === null ? '' : ` — ${decideParkMovedContent(moved)}`) +
|
|
17
|
+
(unsupported === null ? '' : ` — ${checkpointUnsupportedVersionContent(unsupported)}`));
|
|
16
18
|
}
|
|
@@ -14,4 +14,5 @@ export type DisableSkillsDeclaration = {
|
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
16
|
export declare function disableSkillsDeclaration(want: unknown, baseUrl: string | undefined): DisableSkillsDeclaration;
|
|
17
|
+
export declare function disableSkillsDeclarationSettled(want: boolean | null | undefined, baseUrl: string | undefined, withinMs: number): Promise<DisableSkillsDeclaration>;
|
|
17
18
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { engineCapState } from './engineCapsCache.js';
|
|
1
|
+
import { engineCapState, engineCapsSettled } from './engineCapsCache.js';
|
|
2
2
|
const KEY = 'disableSkills';
|
|
3
3
|
const shapeOf = (v) => {
|
|
4
4
|
try {
|
|
@@ -50,3 +50,10 @@ export function disableSkillsDeclaration(want, baseUrl) {
|
|
|
50
50
|
const cause = WITHHELD_CAUSE_OF[state];
|
|
51
51
|
return Object.freeze({ field: Object.freeze({}), withheld: Object.freeze({ cause, text: WITHHELD_TEXT[cause] }) });
|
|
52
52
|
}
|
|
53
|
+
export async function disableSkillsDeclarationSettled(want, baseUrl, withinMs) {
|
|
54
|
+
refuseBadDisableSkills(want, 'disableSkillsDeclarationSettled');
|
|
55
|
+
if (want !== true)
|
|
56
|
+
return disableSkillsDeclaration(want, baseUrl);
|
|
57
|
+
await engineCapsSettled(baseUrl, { withinMs: typeof withinMs === 'number' ? withinMs : 0 });
|
|
58
|
+
return disableSkillsDeclaration(want, baseUrl);
|
|
59
|
+
}
|