blun-king-cli 9.1.597 → 9.1.600
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 +39 -0
- package/README.md +17 -1
- package/agent-spine-plugin/CHANGELOG.md +1 -1
- package/agent-spine-plugin/docs/host-integration.md +1 -15
- package/agent-spine-plugin/docs/preflight-recall.md +1 -1
- package/agent-spine-plugin/scripts/check-install.js +1 -6
- package/agent-spine-plugin/scripts/hermetic-process.js +8 -0
- package/agent-spine-plugin/scripts/release-check.js +2 -3
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +8 -11
- package/agent-spine-plugin/src/hook.js +40 -40
- package/agent-spine-plugin/src/lib/briefing.js +6 -3
- package/agent-spine-plugin/src/lib/hook-context.js +63 -64
- package/agent-spine-plugin/src/lib/hook-output.js +115 -94
- package/agent-spine-plugin/src/lib/hook-timeline.js +20 -11
- package/agent-spine-plugin/src/lib/host-instruction-budget.js +8 -14
- package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +0 -7
- package/agent-spine-plugin/src/lib/pre-answer-timeline-recall.js +104 -0
- package/agent-spine-plugin/src/lib/session-timeline-codex.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +0 -2
- package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +0 -22
- package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-prior.js +42 -50
- package/agent-spine-plugin/src/lib/session-timeline-provider.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline-query.js +0 -1
- package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +0 -4
- package/agent-spine-plugin/src/lib/session-timeline-source.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline-transport.js +0 -8
- package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +0 -3
- package/agent-spine-plugin/src/lib/session-timeline.js +370 -408
- package/agent-spine-plugin/src/lib/source-roots.js +26 -16
- package/agent-spine-plugin/src/lib/timeline-user-feedback.js +0 -1
- package/bin/core-bootstrap.js +18 -10
- package/bin/launcher-mode.js +5 -0
- package/bin/launcher-runtime.js +27 -0
- package/bin/verify-bundled-agent-sources.cjs +28 -2
- package/blun.mjs +798 -143
- package/bundled-agent-sources.json +192 -155
- package/package.json +1 -1
- package/standard-skills/translate-native/README.md +1334 -1
- package/standard-skills/translate-native/SKILL.md +1 -1
- package/standard-skills/translate-native/VERSION +1 -1
- package/standard-skills/translate-native/docs/PREMORTEM.md +901 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +208 -41
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1319 -77
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +66 -11
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +19 -1
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +24 -6
- package/standard-skills/translate-native/integrations/commercial_localization_profile.py +23 -0
- package/standard-skills/translate-native/integrations/website_localization.py +234 -10
- package/standard-skills/translate-native/integrations/website_localization_benchmark.py +39 -3
- package/standard-skills/translate-native/integrations/website_localization_cms.py +512 -10
- package/standard-skills/translate-native/integrations/website_localization_cms_client.py +116 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +101 -11
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +12 -1
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +186 -24
- package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +64 -1
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +528 -5
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth.py +918 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth_runtime.py +609 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_client.py +885 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_http.py +1244 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +220 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_sidecar_adapter.py +331 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_capabilities_http.py +444 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_client.py +673 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch.py +1317 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_client.py +826 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_http.py +1152 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_openapi.py +896 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_runtime.py +621 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_http.py +1180 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_runtime.py +1479 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +133 -17
- package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +319 -4
- package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +72 -2
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +13 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +56 -13
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_openapi.py +335 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +66 -1
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +73 -7
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +93 -9
- package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +221 -11
- package/standard-skills/translate-native/integrations/website_localization_health.py +6 -0
- package/standard-skills/translate-native/integrations/website_localization_http_provider.py +1 -1
- package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +350 -0
- package/standard-skills/translate-native/integrations/website_localization_queue.py +126 -56
- package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +166 -13
- package/standard-skills/translate-native/integrations/website_localization_release.py +423 -17
- package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +168 -13
- package/standard-skills/translate-native/integrations/website_localization_runner.py +10 -0
- package/standard-skills/translate-native/integrations/website_localization_worker.py +213 -9
- package/standard-skills/translate-native/provenance.json +2 -2
- package/standard-skills/translate-native/references/commercial-localization.md +96 -17
- package/standard-skills/translate-native/scripts/blun_language_guard.py +3 -1
- package/standard-skills/translate-native/scripts/check_commercial_review.py +69 -9
- package/standard-skills/translate-native/scripts/commercial_localization_profile.py +829 -31
- package/standard-tools/language-guard/VERSION +1 -1
- package/standard-tools/language-guard/blun_language_guard.py +3 -1
- package/standard-tools/language-guard/check_commercial_review.py +69 -9
- package/standard-tools/language-guard/commercial_localization_profile.py +829 -31
- package/standard-tools/language-guard/provenance.json +2 -2
- package/standard-tools/manifest.json +1 -1
- package/worker-host.mjs +215 -82
- package/agent-spine-plugin/scripts/check-install-king.js +0 -37
|
@@ -7,91 +7,87 @@ import { resolveSessionJob, startOrResumeJob } from "./selfstarter.js";
|
|
|
7
7
|
|
|
8
8
|
const STANDARD_HOST_CONTEXT_BYTES = 9500;
|
|
9
9
|
const MAX_CLAUDE_OVERFLOW_CONTEXT_BYTES = 32 * 1024;
|
|
10
|
-
const CODEX_CONTEXT_ENVELOPE_BYTES = 32 * 1024;
|
|
11
10
|
const ID_RE = /^[A-Za-z0-9][A-Za-z0-9:_.@/-]{0,127}$/;
|
|
12
11
|
export const ATTENTION_WRITE_EVENTS = new Set(["UserPromptSubmit", "PostToolUse", "Stop", "SubagentStop"]);
|
|
13
12
|
const SELFSTART_EVENTS = new Set(["SessionStart", "PostCompact"]);
|
|
14
13
|
|
|
15
14
|
export function boundedId(value, field) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
if (value === null || value === undefined || value === "") return null;
|
|
16
|
+
if (typeof value !== "string" || !ID_RE.test(value)) throw new Error(`${field} is invalid`);
|
|
17
|
+
return value;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
function positiveInteger(value, field) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
if (value === null || value === undefined || value === "") return null;
|
|
22
|
+
const parsed = typeof value === "number"
|
|
23
|
+
? value
|
|
24
|
+
: (typeof value === "string" && /^[1-9][0-9]*$/.test(value) ? Number(value) : Number.NaN);
|
|
25
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) throw new Error(`${field} is invalid`);
|
|
26
|
+
return parsed;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export function promptFromInput(input) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
for (const key of ["prompt", "user_prompt", "message", "input"]) {
|
|
31
|
+
const value = input[key];
|
|
32
|
+
if (typeof value === "string") return value;
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
const text = value
|
|
35
|
+
.filter((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string")
|
|
36
|
+
.map((part) => part.text)
|
|
37
|
+
.join("\n")
|
|
38
|
+
.trim();
|
|
39
|
+
if (text) return text;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
export function hostContextLimit(preflight) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const mode = preflight?.receipt?.instructionBudget?.mode;
|
|
52
|
-
if (mode === "claude-required-overflow") return MAX_CLAUDE_OVERFLOW_CONTEXT_BYTES;
|
|
53
|
-
return STANDARD_HOST_CONTEXT_BYTES;
|
|
46
|
+
if (preflight?.receipt?.instructionHost === "codex") return 32768 + Buffer.byteLength(JSON.stringify(preflight.briefing || {}));
|
|
47
|
+
const mode = preflight?.receipt?.instructionBudget?.mode;
|
|
48
|
+
if (mode === "claude-required-overflow") return MAX_CLAUDE_OVERFLOW_CONTEXT_BYTES;
|
|
49
|
+
return STANDARD_HOST_CONTEXT_BYTES;
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
export function hostFromInput(input) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const explicit = input.host || input.provider || process.env.AGENTSPINE_HOST;
|
|
54
|
+
if (["claude", "codex", "generic"].includes(explicit)) return explicit;
|
|
55
|
+
if ((typeof input.model === "string" && input.model.trim()) || process.env.PLUGIN_ROOT || process.env.CODEX_HOME
|
|
56
|
+
|| process.env.BLUN_PLUGIN_ROOT || process.env.BLUN_HOME) return "codex";
|
|
57
|
+
return "claude";
|
|
62
58
|
}
|
|
63
59
|
|
|
64
60
|
export function gatewayEnvironmentContext(env = process.env) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
61
|
+
if (env.AGENTSPINE_GATEWAY_CONTEXT !== "agentspine.gateway-start/v1") return null;
|
|
62
|
+
const portalRef = boundedId(env.AGENTSPINE_PORTAL_REF, "AGENTSPINE_PORTAL_REF");
|
|
63
|
+
const threadRef = boundedId(env.AGENTSPINE_THREAD_REF, "AGENTSPINE_THREAD_REF");
|
|
64
|
+
if (Boolean(portalRef) !== Boolean(threadRef)) throw new Error("gateway portal and thread references must be paired");
|
|
65
|
+
return {
|
|
66
|
+
host: boundedId(env.AGENTSPINE_HOST, "AGENTSPINE_HOST"),
|
|
67
|
+
entityId: boundedId(env.AGENTSPINE_ENTITY_ID, "AGENTSPINE_ENTITY_ID"),
|
|
68
|
+
groupId: boundedId(env.AGENTSPINE_GROUP_ID, "AGENTSPINE_GROUP_ID"),
|
|
69
|
+
projectId: boundedId(env.AGENTSPINE_PROJECT_ID, "AGENTSPINE_PROJECT_ID"),
|
|
70
|
+
taskId: boundedId(env.AGENTSPINE_TASK_ID, "AGENTSPINE_TASK_ID"),
|
|
71
|
+
queueId: boundedId(env.AGENTSPINE_GATEWAY_QUEUE_ID, "AGENTSPINE_GATEWAY_QUEUE_ID"),
|
|
72
|
+
goalId: boundedId(env.AGENTSPINE_GOAL_ID, "AGENTSPINE_GOAL_ID"),
|
|
73
|
+
goalStepId: boundedId(env.AGENTSPINE_GOAL_STEP_ID, "AGENTSPINE_GOAL_STEP_ID"),
|
|
74
|
+
planDefinitionsDigest: boundedId(env.AGENTSPINE_PLAN_DEFINITIONS_DIGEST,
|
|
75
|
+
"AGENTSPINE_PLAN_DEFINITIONS_DIGEST"),
|
|
76
|
+
gatewayAttempt: positiveInteger(env.AGENTSPINE_GATEWAY_ATTEMPT, "AGENTSPINE_GATEWAY_ATTEMPT"),
|
|
77
|
+
eventId: boundedId(env.AGENTSPINE_CHANNEL_EVENT_ID, "AGENTSPINE_CHANNEL_EVENT_ID"),
|
|
78
|
+
provider: boundedId(env.AGENTSPINE_CHANNEL_PROVIDER, "AGENTSPINE_CHANNEL_PROVIDER"),
|
|
79
|
+
portalRef, threadRef
|
|
80
|
+
};
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
function gatewayBound(gateway, key, suppliedValue, field, allowAbsent = false) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
if (!gateway) return suppliedValue;
|
|
85
|
+
const gatewayValue = gateway[key];
|
|
86
|
+
if (gatewayValue === null || gatewayValue === undefined || gatewayValue === "") {
|
|
87
|
+
if (allowAbsent) return suppliedValue;
|
|
88
|
+
if (suppliedValue !== undefined && suppliedValue !== null && suppliedValue !== "") {
|
|
89
|
+
throw new Error(`${field} does not match the authenticated gateway binding`);
|
|
90
|
+
}
|
|
95
91
|
return null;
|
|
96
92
|
}
|
|
97
93
|
if (suppliedValue !== undefined && suppliedValue !== null
|
|
@@ -163,13 +159,16 @@ export async function runtimeScope(input, root, userStateRoot = null, catalog) {
|
|
|
163
159
|
}
|
|
164
160
|
|
|
165
161
|
export function renderContext(event, catalog, briefing, signal = null, attentionEvent = null, selfstarter = null, channelEvent = null, sourceDiagnostics = null, preflight = null, lessonRecall = null) {
|
|
166
|
-
const
|
|
162
|
+
const partial = sourceDiagnostics?.status === "incomplete" && catalog.summary.total > 0;
|
|
163
|
+
const loaded = sourceDiagnostics?.status === "loaded" || partial;
|
|
167
164
|
const packet = {
|
|
168
165
|
schema: "agentspine.hook-context/v1",
|
|
169
166
|
event,
|
|
170
167
|
priority: ["current-user-request", "explicit-stops", "current-task", "host-rules", "accepted-context", "style-and-relationships"],
|
|
171
168
|
loaded,
|
|
172
|
-
instruction:
|
|
169
|
+
instruction: partial
|
|
170
|
+
? "Apply only listed sources; others are unverified. Inspect sourceResolution; do not retry."
|
|
171
|
+
: loaded
|
|
173
172
|
? "Use this already-loaded briefing now. Do not call an MCP tool to obtain it. The current user request and explicit stops override all remembered style, relationships, and older context."
|
|
174
173
|
: "No host-native source context was loaded. Do not claim personal continuity or recall succeeded. Continue under current native host rules and inspect sourceResolution.",
|
|
175
174
|
signal: signal ? {
|
|
@@ -1,99 +1,119 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const MAX_BYTES=1200;
|
|
2
|
+
const BOUND="\n[optional runtime detail omitted: 1200-byte bound]";
|
|
3
|
+
const bytes=value=>Buffer.byteLength(JSON.stringify(value));
|
|
3
4
|
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
function addRecallField(packet, key, value, maximum = 1020) {
|
|
9
|
-
if (value === undefined || value === null) return;
|
|
10
|
-
packet[key] = value;
|
|
11
|
-
if (byteSize(packet) <= maximum) return true;
|
|
5
|
+
function addRecallField(packet,key,value,maximum=1020){
|
|
6
|
+
if(value===undefined||value===null)return;
|
|
7
|
+
packet[key]=value;
|
|
8
|
+
if(bytes(packet)<=maximum)return true;
|
|
12
9
|
delete packet[key];
|
|
13
10
|
return false;
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
function sharedPrefix(values)
|
|
17
|
-
let prefix
|
|
18
|
-
for
|
|
19
|
-
while
|
|
13
|
+
function sharedPrefix(values){
|
|
14
|
+
let prefix=values[0]||"";
|
|
15
|
+
for(const value of values.slice(1)){
|
|
16
|
+
while(prefix&&!value.startsWith(prefix))prefix=prefix.slice(0,-1);
|
|
20
17
|
}
|
|
21
|
-
return prefix.length
|
|
18
|
+
return prefix.length>=4?prefix:"";
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
function
|
|
25
|
-
if
|
|
26
|
-
if
|
|
27
|
-
const digest
|
|
28
|
-
if
|
|
29
|
-
candidates.common
|
|
30
|
-
candidates.fields.splice(digest,
|
|
31
|
-
for
|
|
21
|
+
function compactRows(packet,candidates){
|
|
22
|
+
if(candidates.rows.length<2)return;
|
|
23
|
+
if(!candidates.prefixes){
|
|
24
|
+
const digest=candidates.fields.indexOf("digest");
|
|
25
|
+
if(digest>=0&&candidates.rows.every(row=>row[digest]===candidates.rows[0][digest])){
|
|
26
|
+
candidates.common={...(candidates.common||{}),digest:candidates.rows[0][digest]};
|
|
27
|
+
candidates.fields.splice(digest,1);
|
|
28
|
+
for(const row of candidates.rows)row.splice(digest,1);
|
|
32
29
|
}
|
|
33
|
-
candidates.prefixes
|
|
34
|
-
const values
|
|
35
|
-
const prefix
|
|
36
|
-
:
|
|
37
|
-
?
|
|
38
|
-
if
|
|
39
|
-
for
|
|
30
|
+
candidates.prefixes=candidates.fields.map((field,index)=>{
|
|
31
|
+
const values=candidates.rows.map(row=>row[index]);
|
|
32
|
+
const prefix=["id","message","session"].includes(field)?sharedPrefix(values)
|
|
33
|
+
:field==="at"&&values.every(value=>/^\d{4}-\d{2}-\d{2}T\d{2}:/.test(value))
|
|
34
|
+
?values[0].slice(0,14):"";
|
|
35
|
+
if(!prefix||!values.every(value=>value.startsWith(prefix)))return"";
|
|
36
|
+
for(const row of candidates.rows)row[index]=row[index].slice(prefix.length);
|
|
40
37
|
return prefix;
|
|
41
38
|
});
|
|
42
39
|
}
|
|
43
|
-
if
|
|
40
|
+
if(packet.task){
|
|
44
41
|
delete packet.task.status;
|
|
45
|
-
if
|
|
42
|
+
if(packet.task.lastVerifiedStep)delete packet.task.lastVerifiedStep.result;
|
|
46
43
|
}
|
|
47
|
-
const review
|
|
48
|
-
if
|
|
49
|
-
status:
|
|
50
|
-
question:
|
|
44
|
+
const review=packet.feedbackReview;
|
|
45
|
+
if(review?.modelClarification)packet.feedbackReview={
|
|
46
|
+
status:review.status,completionVerified:false,target:review.targetAssertionId,
|
|
47
|
+
question:review.modelClarification.question,model:true
|
|
51
48
|
};
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
function
|
|
55
|
-
const candidates
|
|
56
|
-
const proposal
|
|
57
|
-
if
|
|
58
|
-
if
|
|
59
|
-
&&
|
|
60
|
-
|
|
51
|
+
function compactFeedback(packet){
|
|
52
|
+
const candidates=packet.feedbackCandidates;
|
|
53
|
+
const proposal=packet.feedbackReview?.modelClarification;
|
|
54
|
+
if(proposal)for(const key of ["completionVerified","provider","replaces"])delete proposal[key];
|
|
55
|
+
if(candidates&&!Array.isArray(candidates)
|
|
56
|
+
&&Array.isArray(candidates.fields)&&Array.isArray(candidates.rows)){
|
|
57
|
+
compactRows(packet,candidates);
|
|
61
58
|
return true;
|
|
62
59
|
}
|
|
63
|
-
if
|
|
64
|
-
const origins
|
|
65
|
-
const sharedKeys
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
packet.feedbackCandidates = {
|
|
60
|
+
if(!Array.isArray(candidates)||candidates.length<2)return false;
|
|
61
|
+
const origins=["provider","session"];
|
|
62
|
+
const sharedKeys=origins.filter(key=>candidates.every(item=>item?.[key]===candidates[0]?.[key]));
|
|
63
|
+
const fields=["id","text","digest","message","at",
|
|
64
|
+
...origins.filter(key=>!sharedKeys.includes(key))];
|
|
65
|
+
packet.feedbackCandidates={
|
|
70
66
|
fields,
|
|
71
|
-
...(sharedKeys.length
|
|
72
|
-
[key,
|
|
73
|
-
rows:
|
|
67
|
+
...(sharedKeys.length?{common:Object.fromEntries(sharedKeys.map(key=>
|
|
68
|
+
[key,candidates[0][key]]))}:{}),
|
|
69
|
+
rows:candidates.map(item=>fields.map(key=>item[key]))
|
|
74
70
|
};
|
|
75
|
-
|
|
71
|
+
compactRows(packet,packet.feedbackCandidates);
|
|
76
72
|
return true;
|
|
77
73
|
}
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
function timelineRecall(t){
|
|
76
|
+
if(!t.events?.length)return{status:t.status,reason:t.reason,awaited:t.awaited,
|
|
77
|
+
reads:t.sourceReads,completionVerified:false,omitted:t.omittedEvents||0};
|
|
78
|
+
return{status:t.status,awaited:t.awaited,reads:t.sourceReads,completionVerified:false,
|
|
79
|
+
naturalMessages:"unresolved",trust:"untrusted-session-history",
|
|
80
|
+
...(t.source?{source:t.source}:{sources:t.sources}),prefixes:t.prefixes,
|
|
81
|
+
fields:t.fields,rows:t.events.map(row=>[...row]),omitted:t.omittedEvents||0}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function preAnswerRecallCapsule(detailed){
|
|
85
|
+
if(detailed?.event!=="UserPromptSubmit"||!detailed.loaded)return null;
|
|
86
|
+
const briefing=detailed.briefing;
|
|
87
|
+
const preflight=detailed.preflight?.briefing;
|
|
88
|
+
if(!briefing||!preflight)return null;
|
|
89
|
+
if(briefing.scope?.groupId!==null&&briefing.scope?.groupId!==undefined)return null;
|
|
90
|
+
const omitted=[];
|
|
91
|
+
const packet=briefing.preAnswerRecall?structuredClone(briefing.preAnswerRecall):{
|
|
92
|
+
schema:"agentspine.pre-answer-recall/v1",
|
|
93
|
+
order:"review-before-claims-and-actions",
|
|
94
|
+
authority:"context-only"
|
|
90
95
|
};
|
|
96
|
+
const timeline=detailed.sourceResolution?.timeline?.preAnswerRecall;
|
|
97
|
+
if(timeline?.awaited){
|
|
98
|
+
let candidate=timelineRecall(timeline);
|
|
99
|
+
while(!addRecallField(packet,"timeline",candidate)&&candidate.rows?.length){
|
|
100
|
+
candidate.rows.pop();
|
|
101
|
+
candidate.omitted += 1;
|
|
102
|
+
if(!candidate.rows.length)candidate={status:"unavailable",reason:"host-context-budget",
|
|
103
|
+
awaited:candidate.awaited,reads:candidate.reads,completionVerified:false,omitted:candidate.omitted};
|
|
104
|
+
}
|
|
105
|
+
if(!packet.timeline&&bytes(packet)>MAX_BYTES){
|
|
106
|
+
const keys=Object.keys(packet).filter(key=>!/^schema$|^order$|^authority$/.test(key));
|
|
107
|
+
for(const key of keys)delete packet[key];
|
|
108
|
+
packet.timeline=candidate;packet.omitted=keys;
|
|
109
|
+
}else if(!packet.timeline)packet.timelineOmitted=true;
|
|
110
|
+
}
|
|
91
111
|
const mustRemember = (preflight.mustRemember || []).map((item) => ({
|
|
92
112
|
id: item.id, claim: item.claim, checksum: item.checksum
|
|
93
113
|
}));
|
|
94
114
|
if (mustRemember.length && !addRecallField(packet, "mustRemember", mustRemember)) {
|
|
95
|
-
if (!
|
|
96
|
-
|| !addRecallField(packet, "mustRemember", mustRemember,
|
|
115
|
+
if (!compactFeedback(packet)
|
|
116
|
+
|| !addRecallField(packet, "mustRemember", mustRemember, MAX_BYTES)) {
|
|
97
117
|
omitted.push("mustRemember");
|
|
98
118
|
}
|
|
99
119
|
}
|
|
@@ -101,13 +121,13 @@ export function preAnswerRecallCapsule(detailed) {
|
|
|
101
121
|
(provider.items || []).map((item) => ({ providerId: provider.providerId,
|
|
102
122
|
id: item.id, revision: item.revision, claim: item.claim, source: item.source }))).slice(0, 3);
|
|
103
123
|
if (retrieval.length && !addRecallField(packet, "retrieval", retrieval)) {
|
|
104
|
-
if (!
|
|
105
|
-
|| !addRecallField(packet, "retrieval", retrieval,
|
|
124
|
+
if (!compactFeedback(packet)
|
|
125
|
+
|| !addRecallField(packet, "retrieval", retrieval, MAX_BYTES)) {
|
|
106
126
|
omitted.push("retrieval");
|
|
107
127
|
}
|
|
108
128
|
}
|
|
109
|
-
|
|
110
|
-
if
|
|
129
|
+
compactFeedback(packet);
|
|
130
|
+
if(omitted.length&&bytes({...packet,omitted})<=MAX_BYTES)packet.omitted=omitted;
|
|
111
131
|
return Object.keys(packet).length > 3 ? packet : null;
|
|
112
132
|
}
|
|
113
133
|
|
|
@@ -166,11 +186,10 @@ export function blunRuntimeContext(context) {
|
|
|
166
186
|
|
|
167
187
|
export function blunRuntimeMessage(context) {
|
|
168
188
|
const runtime = JSON.parse(blunRuntimeContext(context));
|
|
169
|
-
const warning = runtime.sourceResolution?.incomplete ? ` Warning: ${runtime.sourceResolution.warning}` : "";
|
|
170
189
|
const base = runtime.loaded
|
|
171
190
|
? runtime.preAnswerRecall
|
|
172
|
-
? `AgentSpine ready: ${runtime.indexedSources} sources indexed
|
|
173
|
-
: `AgentSpine ready: ${runtime.indexedSources} sources indexed. Load detailed continuity only on demand through session_briefing
|
|
191
|
+
? `AgentSpine ready: ${runtime.indexedSources} sources indexed.`
|
|
192
|
+
: `AgentSpine ready: ${runtime.indexedSources} sources indexed. Load detailed continuity only on demand through session_briefing.`
|
|
174
193
|
: `AgentSpine unavailable${runtime.sourceResolution?.reason ? `: ${runtime.sourceResolution.reason}` : ""}. ${runtime.instruction}`;
|
|
175
194
|
const active = {};
|
|
176
195
|
if (runtime.signal && (runtime.signal.captured || runtime.signal.accepted
|
|
@@ -195,22 +214,25 @@ export function blunRuntimeMessage(context) {
|
|
|
195
214
|
: runtime.premortem?.instruction;
|
|
196
215
|
const premortem = instruction ? `\n${instruction}` : "";
|
|
197
216
|
const message = `${base}${recall}${details}${premortem}`;
|
|
198
|
-
if (Buffer.byteLength(message) <=
|
|
199
|
-
const compact = `${base}${recall}${
|
|
200
|
-
if (Buffer.byteLength(compact) <=
|
|
217
|
+
if (Buffer.byteLength(message) <= MAX_BYTES) return message;
|
|
218
|
+
const compact = `${base}${recall}${BOUND}\n${compactPremortemRegistration(runtime.premortem)}`;
|
|
219
|
+
if (Buffer.byteLength(compact) <= MAX_BYTES) return compact;
|
|
201
220
|
if (!runtime.preAnswerRecall) {
|
|
202
|
-
return `${base}${
|
|
221
|
+
return `${base}${BOUND}\n${compactPremortemRegistration(runtime.premortem, false)}`;
|
|
203
222
|
}
|
|
204
|
-
const recallOnly = `${base}${recall}${
|
|
205
|
-
if (Buffer.byteLength(recallOnly) <=
|
|
206
|
-
return Buffer.byteLength(recall)<=
|
|
223
|
+
const recallOnly = `${base}${recall}${BOUND}`;
|
|
224
|
+
if (Buffer.byteLength(recallOnly) <= MAX_BYTES) return recallOnly;
|
|
225
|
+
return Buffer.byteLength(recall)<=MAX_BYTES+1?recall.slice(1):`${base}\nRecall unavailable.`;
|
|
207
226
|
}
|
|
208
227
|
|
|
209
|
-
export function hookOutput(event, context, env = process.env) {
|
|
228
|
+
export function hookOutput(event, context, env = process.env, sourceWarning = null) {
|
|
210
229
|
if (env.BLUN_PLUGIN_ROOT) {
|
|
211
|
-
|
|
230
|
+
const source=JSON.parse(context).sourceResolution,w=sourceWarning||(source?.incomplete&&source.warning);
|
|
231
|
+
return {hookSpecificOutput:{hookEventName:event,additionalContext:blunRuntimeMessage(context),
|
|
232
|
+
...(w?{message:`AgentSpine source warning: ${w}`}:{})}};
|
|
212
233
|
}
|
|
213
|
-
return { hookSpecificOutput: { hookEventName: event, additionalContext: context
|
|
234
|
+
return { hookSpecificOutput: { hookEventName: event, additionalContext: context,
|
|
235
|
+
...(sourceWarning ? { message: sourceWarning } : {}) } };
|
|
214
236
|
}
|
|
215
237
|
|
|
216
238
|
export function blockedHookOutput(event, reason, env = process.env) {
|
|
@@ -234,15 +256,12 @@ export function blockStop(event, reason) {
|
|
|
234
256
|
process.stdout.write(`${JSON.stringify(blockedHookOutput(event, reason))}\n`);
|
|
235
257
|
}
|
|
236
258
|
|
|
237
|
-
export function lifecycleOutput(event, artifactGuard, premortem,
|
|
259
|
+
export function lifecycleOutput(event, artifactGuard, premortem, delivery = null, env = process.env,
|
|
238
260
|
sourceWarning = null, lessonRecall = null) {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
if (deliveryVerification?.status === "test-failed" && deliveryVerification.reason) {
|
|
242
|
-
messages.push(deliveryVerification.reason);
|
|
243
|
-
}
|
|
261
|
+
const w=[sourceWarning&&`AgentSpine source warning: ${sourceWarning}`,artifactGuard?.reason,
|
|
262
|
+
delivery?.status==="test-failed"&&delivery.reason].filter(Boolean),c=[];
|
|
244
263
|
if (/^[a-f0-9]{64}$/.test(premortem?.writeDigest || "")) {
|
|
245
|
-
|
|
264
|
+
c.push([
|
|
246
265
|
premortem.writeIntent
|
|
247
266
|
? "AgentSpine recorded the allowed mutation intent for the delivery premortem."
|
|
248
267
|
: "AgentSpine recorded the direct write for the delivery premortem.",
|
|
@@ -250,7 +269,7 @@ export function lifecycleOutput(event, artifactGuard, premortem, deliveryVerific
|
|
|
250
269
|
].join("\n"));
|
|
251
270
|
}
|
|
252
271
|
if (lessonRecall?.status === "recalled") {
|
|
253
|
-
|
|
272
|
+
c.push(JSON.stringify({
|
|
254
273
|
schema: lessonRecall.schema, receiptDigest: lessonRecall.receiptDigest,
|
|
255
274
|
instruction: lessonRecall.instruction, items: lessonRecall.items,
|
|
256
275
|
learning: lessonRecall.learning || [], learningDiagnostics: lessonRecall.learningDiagnostics || null,
|
|
@@ -258,6 +277,8 @@ export function lifecycleOutput(event, artifactGuard, premortem, deliveryVerific
|
|
|
258
277
|
authority: "context-only"
|
|
259
278
|
}));
|
|
260
279
|
}
|
|
261
|
-
|
|
262
|
-
|
|
280
|
+
const o={hookEventName:event},f=env.BLUN_PLUGIN_ROOT?"message":"additionalContext";
|
|
281
|
+
if(w.length)o[f]=w.join("\n");
|
|
282
|
+
if(c.length)o.additionalContext=[o.additionalContext,...c].filter(Boolean).join("\n");
|
|
283
|
+
return Object.keys(o).length>1?{hookSpecificOutput:o}:{};
|
|
263
284
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { sessionTimelineLifecycleHint } from "./session-timeline.js";
|
|
2
2
|
import { issueHostTranscriptReceipt } from "./session-timeline-enrollment.js";
|
|
3
3
|
import { consumeTimelineHostOrigin } from "./session-timeline-host-origin.js";
|
|
4
|
+
import {
|
|
5
|
+
automaticPreAnswerTimelineRecall, timelineRecallNotFound
|
|
6
|
+
} from "./pre-answer-timeline-recall.js";
|
|
4
7
|
import {
|
|
5
8
|
timelineHostForRuntime, timelineHostHome, timelineProtocolFromRuntime, timelineSourceFromRuntime
|
|
6
9
|
} from "./session-timeline-provider.js";
|
|
@@ -21,7 +24,6 @@ function hasRawTimelineGroupSignal(input) {
|
|
|
21
24
|
}
|
|
22
25
|
return values.some((value) => value !== undefined && value !== null && value !== "");
|
|
23
26
|
} catch {
|
|
24
|
-
// Parser-shaped uncertainty must not turn a lifecycle hint into a denial.
|
|
25
27
|
return false;
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -36,8 +38,6 @@ function receiptObservation(receipt) {
|
|
|
36
38
|
expiresAt: receipt.expiresAt || null, authority: AUTHORITY };
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
// This observes a provider hook payload but never returns the
|
|
40
|
-
// opaque enrollment token through model-visible context.
|
|
41
41
|
export async function observeHostTranscriptReceipt({ root, event, input, scope, hostHome, hostOrigin = null, clock = null }) {
|
|
42
42
|
if (event !== "UserPromptSubmit") return receiptObservation({ status: "not-applicable" });
|
|
43
43
|
if (hasRawTimelineGroupSignal(input)) return receiptObservation({ status: "unavailable", reason: "raw-group-scope" });
|
|
@@ -50,7 +50,9 @@ export async function observeHostTranscriptReceipt({ root, event, input, scope,
|
|
|
50
50
|
return receiptObservation(receipt);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export async function captureSessionTimelineLifecycle({
|
|
53
|
+
export async function captureSessionTimelineLifecycle({
|
|
54
|
+
root, event, input, scope, hostHome, hostOrigin = null, clock = null, turnId = null, prompt
|
|
55
|
+
}) {
|
|
54
56
|
if (hasRawTimelineGroupSignal(input)) return groupSuppressed("raw-group-scope");
|
|
55
57
|
if (!scope || scope.groupId !== null) return groupSuppressed("computed-group-scope");
|
|
56
58
|
let hostReceipt = null;
|
|
@@ -64,14 +66,21 @@ export async function captureSessionTimelineLifecycle({ root, event, input, scop
|
|
|
64
66
|
const result = (value) => hostReceipt ? { ...value, hostReceipt } : value;
|
|
65
67
|
const host = timelineHostForRuntime(scope.host, process.env);
|
|
66
68
|
if (!host || (host === "king" && !timelineProtocolFromRuntime(host))) return unavailable("host-not-supported");
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
const session = input.session_id ?? input.sessionId;
|
|
70
|
+
const lifecycle = await sessionTimelineLifecycleHint({ root, host,
|
|
71
|
+
sessionId: session, scope, environment: process.env });
|
|
72
|
+
if (event !== "UserPromptSubmit" || !hostOrigin) return result(lifecycle);
|
|
73
|
+
const preAnswerRecall = lifecycle.priorSessions?.available
|
|
74
|
+
? await automaticPreAnswerTimelineRecall({
|
|
75
|
+
root, host, sessionId: session, scope, hostHome, eventId: input.event_id ?? input.hook_event_id,
|
|
76
|
+
turnId, prompt,
|
|
77
|
+
environment: process.env
|
|
78
|
+
}) : timelineRecallNotFound();
|
|
79
|
+
const prior = lifecycle.priorSessions;
|
|
80
|
+
return result({ ...lifecycle, priorSessions: prior && { available: prior.available, sessions: prior.sessions,
|
|
81
|
+
indexedEvents: prior.indexedEvents, freshness: prior.freshness, authority: prior.authority }, preAnswerRecall });
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
// The calling hook performs this only at its final pre-answer boundary. The
|
|
72
|
-
// origin helper consumes the exact signed preflight receipt before this can
|
|
73
|
-
// touch timeline receipt state. Failure to form a timeline origin is optional
|
|
74
|
-
// and yields no opaque receipt; failure to consume preflight remains blocking.
|
|
75
84
|
export async function finalizeUserPromptSessionTimeline({
|
|
76
85
|
root, input, scope, resolvedSources, preflight, prompt, now = new Date()
|
|
77
86
|
}) {
|
|
@@ -83,7 +92,7 @@ export async function finalizeUserPromptSessionTimeline({
|
|
|
83
92
|
return { preflightConsumed: true, briefingOrigin: consumed.briefingOrigin,
|
|
84
93
|
timeline: await captureSessionTimelineLifecycle({
|
|
85
94
|
root, event: "UserPromptSubmit", input, scope, hostHome: resolvedSources.hostHome,
|
|
86
|
-
hostOrigin: consumed.origin, clock: () => new Date(now)
|
|
95
|
+
hostOrigin: consumed.origin, clock: () => new Date(now), turnId: preflight.receipt.id, prompt
|
|
87
96
|
}) };
|
|
88
97
|
} catch (error) {
|
|
89
98
|
return { preflightConsumed: true, briefingOrigin: consumed.briefingOrigin, timeline: {
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
const
|
|
2
|
-
export const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export function isKingHost(env = process.env) {
|
|
6
|
-
return Boolean(env.BLUN_PLUGIN_ROOT && env.BLUN_HOME);
|
|
7
|
-
}
|
|
1
|
+
const STANDARD = 8192;
|
|
2
|
+
export const KING_SOURCE_BYTES = 4194304;
|
|
3
|
+
const KING_TOTAL = 8388608;
|
|
4
|
+
export const isKingHost = (env = process.env) => !!(env.BLUN_PLUGIN_ROOT && env.BLUN_HOME);
|
|
8
5
|
|
|
9
6
|
export function instructionBudget(host, usedBytes = 0, env = process.env) {
|
|
10
|
-
const hardLimitBytes = host === "codex" && isKingHost(env) ?
|
|
11
|
-
: host === "claude" ?
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
mode: overflowBytes ? `${host}-required-overflow` : "standard",
|
|
15
|
-
standardBytes: STANDARD_BYTES, hardLimitBytes, usedBytes, overflowBytes
|
|
16
|
-
};
|
|
7
|
+
const hardLimitBytes = host === "codex" && isKingHost(env) ? KING_TOTAL
|
|
8
|
+
: host === "claude" ? 16384 : host === "codex" ? 32768 : STANDARD;
|
|
9
|
+
return { mode: usedBytes > STANDARD ? `${host}-required-overflow` : "standard",
|
|
10
|
+
standardBytes: STANDARD, hardLimitBytes, usedBytes, overflowBytes: Math.max(0, usedBytes - STANDARD) };
|
|
17
11
|
}
|
|
@@ -51,9 +51,6 @@ export function timelineInterpretationRequest(value) {
|
|
|
51
51
|
return structuredClone(value);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
// Hosts qualify MCP tools differently. These prefixes must stay exact: a
|
|
55
|
-
// foreign MCP server can expose an identically named tool but must never
|
|
56
|
-
// receive a timeline invocation permit.
|
|
57
54
|
export const AGENTSPINE_TIMELINE_TOOL_PREFIX = "mcp__plugin_agent-spine_agent-spine__";
|
|
58
55
|
export const BLUN_TIMELINE_TOOL_PREFIX = "mcp__agent-spine__";
|
|
59
56
|
|
|
@@ -138,10 +135,6 @@ function resolvedClaims(args) {
|
|
|
138
135
|
return result;
|
|
139
136
|
}
|
|
140
137
|
|
|
141
|
-
// Direct stdio callers can bypass the MCP schema. Merge the flat and nested
|
|
142
|
-
// representations only when every present claim agrees. In particular, a
|
|
143
|
-
// nested group claim stays visible when a top-level argument tries to clear
|
|
144
|
-
// it with null, so no existing private permit crosses into a group turn.
|
|
145
138
|
export function timelineInvocationInput(args = {}) {
|
|
146
139
|
return resolvedClaims(args);
|
|
147
140
|
}
|