@kashscript/hudhud 0.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/LICENSE +133 -0
- package/README.md +67 -0
- package/dist/adapters/prisma.d.ts +170 -0
- package/dist/adapters/prisma.d.ts.map +1 -0
- package/dist/adapters/prisma.js +185 -0
- package/dist/adapters/prisma.js.map +1 -0
- package/dist/anchor/index.d.ts +55 -0
- package/dist/anchor/index.d.ts.map +1 -0
- package/dist/anchor/index.js +97 -0
- package/dist/anchor/index.js.map +1 -0
- package/dist/certificate/certificate.d.ts +115 -0
- package/dist/certificate/certificate.d.ts.map +1 -0
- package/dist/certificate/certificate.js +221 -0
- package/dist/certificate/certificate.js.map +1 -0
- package/dist/certificate/html.d.ts +10 -0
- package/dist/certificate/html.d.ts.map +1 -0
- package/dist/certificate/html.js +141 -0
- package/dist/certificate/html.js.map +1 -0
- package/dist/certificate/index.d.ts +3 -0
- package/dist/certificate/index.d.ts.map +1 -0
- package/dist/certificate/index.js +5 -0
- package/dist/certificate/index.js.map +1 -0
- package/dist/did-registry/index.d.ts +3 -0
- package/dist/did-registry/index.d.ts.map +1 -0
- package/dist/did-registry/index.js +10 -0
- package/dist/did-registry/index.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +65 -0
- package/dist/errors.js.map +1 -0
- package/dist/exception/index.d.ts +97 -0
- package/dist/exception/index.d.ts.map +1 -0
- package/dist/exception/index.js +144 -0
- package/dist/exception/index.js.map +1 -0
- package/dist/identity/index.d.ts +33 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +36 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/event-schema.d.ts +97 -0
- package/dist/ledger/event-schema.d.ts.map +1 -0
- package/dist/ledger/event-schema.js +54 -0
- package/dist/ledger/event-schema.js.map +1 -0
- package/dist/ledger/index.d.ts +7 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +14 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/mutex.d.ts +9 -0
- package/dist/ledger/mutex.d.ts.map +1 -0
- package/dist/ledger/mutex.js +36 -0
- package/dist/ledger/mutex.js.map +1 -0
- package/dist/ledger/store.d.ts +31 -0
- package/dist/ledger/store.d.ts.map +1 -0
- package/dist/ledger/store.js +66 -0
- package/dist/ledger/store.js.map +1 -0
- package/dist/ledger/writer.d.ts +69 -0
- package/dist/ledger/writer.d.ts.map +1 -0
- package/dist/ledger/writer.js +167 -0
- package/dist/ledger/writer.js.map +1 -0
- package/dist/mcp/allowlist.d.ts +53 -0
- package/dist/mcp/allowlist.d.ts.map +1 -0
- package/dist/mcp/allowlist.js +65 -0
- package/dist/mcp/allowlist.js.map +1 -0
- package/dist/mcp/client.d.ts +68 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +198 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +5 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/privacy/index.d.ts +21 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +96 -0
- package/dist/privacy/index.js.map +1 -0
- package/dist/runtime/dispatcher.d.ts +27 -0
- package/dist/runtime/dispatcher.d.ts.map +1 -0
- package/dist/runtime/dispatcher.js +66 -0
- package/dist/runtime/dispatcher.js.map +1 -0
- package/dist/runtime/escalation.d.ts +98 -0
- package/dist/runtime/escalation.d.ts.map +1 -0
- package/dist/runtime/escalation.js +188 -0
- package/dist/runtime/escalation.js.map +1 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +9 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/runner.d.ts +78 -0
- package/dist/runtime/runner.d.ts.map +1 -0
- package/dist/runtime/runner.js +145 -0
- package/dist/runtime/runner.js.map +1 -0
- package/dist/scope/index.d.ts +19 -0
- package/dist/scope/index.d.ts.map +1 -0
- package/dist/scope/index.js +44 -0
- package/dist/scope/index.js.map +1 -0
- package/dist/sop/compiler.d.ts +36 -0
- package/dist/sop/compiler.d.ts.map +1 -0
- package/dist/sop/compiler.js +54 -0
- package/dist/sop/compiler.js.map +1 -0
- package/dist/sop/index.d.ts +3 -0
- package/dist/sop/index.d.ts.map +1 -0
- package/dist/sop/index.js +5 -0
- package/dist/sop/index.js.map +1 -0
- package/dist/sop/service.d.ts +40 -0
- package/dist/sop/service.d.ts.map +1 -0
- package/dist/sop/service.js +140 -0
- package/dist/sop/service.js.map +1 -0
- package/package.json +112 -0
- package/src/adapters/prisma.ts +310 -0
- package/src/anchor/index.ts +149 -0
- package/src/certificate/certificate.ts +340 -0
- package/src/certificate/html.ts +156 -0
- package/src/certificate/index.ts +4 -0
- package/src/did-registry/index.ts +11 -0
- package/src/errors.ts +79 -0
- package/src/exception/index.ts +244 -0
- package/src/identity/index.ts +82 -0
- package/src/index.ts +33 -0
- package/src/ledger/event-schema.ts +61 -0
- package/src/ledger/index.ts +22 -0
- package/src/ledger/mutex.ts +37 -0
- package/src/ledger/store.ts +86 -0
- package/src/ledger/writer.ts +249 -0
- package/src/mcp/allowlist.ts +102 -0
- package/src/mcp/client.ts +284 -0
- package/src/mcp/index.ts +4 -0
- package/src/privacy/index.ts +123 -0
- package/src/runtime/dispatcher.ts +96 -0
- package/src/runtime/escalation.ts +292 -0
- package/src/runtime/index.ts +14 -0
- package/src/runtime/runner.ts +227 -0
- package/src/scope/index.ts +59 -0
- package/src/sop/compiler.ts +99 -0
- package/src/sop/index.ts +4 -0
- package/src/sop/service.ts +184 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// @kashscript/hudhud/runtime — NeuronToolDispatcher
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// The seam where neuron's DAGExecutor reaches the host's real MCP tool
|
|
5
|
+
// transport, with the accountability append done INLINE on the awaited dispatch
|
|
6
|
+
// path. Neuron's `onEvent` is synchronous fire-and-forget (no back-pressure) —
|
|
7
|
+
// so the signed ledger row is appended HERE, inside the awaited `dispatch`, so
|
|
8
|
+
// a failed append fails the node and the run can never observe an unrecorded
|
|
9
|
+
// tool result. Ported from the oreoasis-core-host engine (M5); telemetry dropped.
|
|
10
|
+
// ============================================================================
|
|
11
|
+
import { ToolDispatchError } from "@kashscript/neuron/kernel";
|
|
12
|
+
export class NeuronToolDispatcher {
|
|
13
|
+
deps;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.deps = deps;
|
|
16
|
+
}
|
|
17
|
+
/** Resolve a tool name to its manifest, or null when unbound. */
|
|
18
|
+
resolve(toolName) {
|
|
19
|
+
return this.deps.bindings.get(toolName)?.manifest ?? null;
|
|
20
|
+
}
|
|
21
|
+
/** Execute one tool call: resolve binding → real transport → (on JSON-RPC
|
|
22
|
+
* error) EXECUTOR_FAILED (no row) → INLINE signed ledger append → return. */
|
|
23
|
+
async dispatch(input) {
|
|
24
|
+
const toolName = input.tool.name;
|
|
25
|
+
const binding = this.deps.bindings.get(toolName);
|
|
26
|
+
if (binding === undefined) {
|
|
27
|
+
throw new ToolDispatchError(`NeuronToolDispatcher: no binding registered for tool '${toolName}' (dagNodeId=${input.dagNodeId})`, "TOOL_NOT_FOUND");
|
|
28
|
+
}
|
|
29
|
+
const mcpToolName = binding.mcpToolName ?? toolName;
|
|
30
|
+
const transport = await this.deps.mcpClientPool.executeMcpToolCall(binding.mcpEndpointUrl, mcpToolName, input.parameters);
|
|
31
|
+
if (!transport.ok) {
|
|
32
|
+
// audit r1 F8: a money-moving tool can fail AFTER a partial side-effect, so
|
|
33
|
+
// record the FAILED dispatch on the signed ledger BEFORE throwing — every
|
|
34
|
+
// dispatch (success or application-error) leaves an accountable row.
|
|
35
|
+
await this.deps.forensicLedger.appendActorTransition({
|
|
36
|
+
sessionId: this.deps.sessionId,
|
|
37
|
+
payloadMutation: {
|
|
38
|
+
event: "tool.failed",
|
|
39
|
+
tool: toolName,
|
|
40
|
+
dagNodeId: input.dagNodeId,
|
|
41
|
+
jsonRpcId: transport.jsonRpcId,
|
|
42
|
+
errorCode: String(transport.error.code),
|
|
43
|
+
errorMessage: transport.error.message,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
throw new ToolDispatchError(`NeuronToolDispatcher: MCP tool '${mcpToolName}' returned a JSON-RPC error (code=${transport.error.code}): ${transport.error.message}`, "EXECUTOR_FAILED");
|
|
47
|
+
}
|
|
48
|
+
// INLINE accountability append — BEFORE returning. A throw here fails the
|
|
49
|
+
// node (the executor awaits us), so no unrecorded result can be observed.
|
|
50
|
+
await this.deps.forensicLedger.appendActorTransition({
|
|
51
|
+
sessionId: this.deps.sessionId,
|
|
52
|
+
payloadMutation: {
|
|
53
|
+
event: "tool.dispatch",
|
|
54
|
+
tool: toolName,
|
|
55
|
+
dagNodeId: input.dagNodeId,
|
|
56
|
+
jsonRpcId: transport.jsonRpcId,
|
|
57
|
+
result: transport.result,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
output: transport.result,
|
|
62
|
+
cost: { usdMicros: input.tool.costHint.usdMicros, latencyMs: transport.latencyMs },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/runtime/dispatcher.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,oDAAoD;AACpD,+EAA+E;AAC/E,uEAAuE;AACvE,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,kFAAkF;AAClF,+EAA+E;AAE/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAsB9D,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,IAA8B;QAA9B,SAAI,GAAJ,IAAI,CAA0B;IAAG,CAAC;IAE/D,iEAAiE;IACjE,OAAO,CAAC,QAAgB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC5D,CAAC;IAED;kFAC8E;IAC9E,KAAK,CAAC,QAAQ,CAAC,KAAoB;QACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,iBAAiB,CACzB,yDAAyD,QAAQ,gBAAgB,KAAK,CAAC,SAAS,GAAG,EACnG,gBAAgB,CACjB,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,QAAQ,CAAC;QACpD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,OAAO,CAAC,cAAc,EAAE,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAE1H,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,4EAA4E;YAC5E,0EAA0E;YAC1E,qEAAqE;YACrE,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAAC;gBACnD,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS;gBAC9B,eAAe,EAAE;oBACf,KAAK,EAAE,aAAa;oBACpB,IAAI,EAAE,QAAQ;oBACd,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,SAAS,EAAE,SAAS,CAAC,SAAS;oBAC9B,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;oBACvC,YAAY,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO;iBACtC;aACF,CAAC,CAAC;YACH,MAAM,IAAI,iBAAiB,CACzB,mCAAmC,WAAW,qCAAqC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,EACtI,iBAAiB,CAClB,CAAC;QACJ,CAAC;QAED,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAAC;YACnD,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS;YAC9B,eAAe,EAAE;gBACf,KAAK,EAAE,eAAe;gBACtB,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,SAAS,EAAE,SAAS,CAAC,SAAS;gBAC9B,MAAM,EAAE,SAAS,CAAC,MAAM;aACzB;SACF,CAAC,CAAC;QAEH,OAAO;YACL,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE;SACnF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { SignedActionEnvelope, ToolCall } from "@kashscript/neuron/handshake";
|
|
2
|
+
import type { ExceptionClassificationInput, ExceptionClassificationRecord, ExceptionClassificationStore } from "../exception";
|
|
3
|
+
import type { LedgerStore } from "../ledger/store";
|
|
4
|
+
import type { ForensicLedgerService } from "../ledger/writer";
|
|
5
|
+
export type EscalationErrorCode = "AGENT_DID_INVALID" | "OPERATOR_DID_INVALID" | "AGENT_OPERATOR_NOT_DISTINCT" | "RESUME_ALREADY_CLAIMED";
|
|
6
|
+
export declare class EscalationError extends Error {
|
|
7
|
+
readonly code: EscalationErrorCode;
|
|
8
|
+
readonly detail: Readonly<Record<string, unknown>>;
|
|
9
|
+
constructor(code: EscalationErrorCode, message: string, detail?: Readonly<Record<string, unknown>>);
|
|
10
|
+
}
|
|
11
|
+
export interface BuildHumanApprovalEnvelopeInput {
|
|
12
|
+
readonly pendingCall: ToolCall;
|
|
13
|
+
readonly dagId: string;
|
|
14
|
+
readonly blueprintId: string;
|
|
15
|
+
/** The AGENT identity — the host's own key. `did` is the ledger DID (the
|
|
16
|
+
* signature `kid`); `publicKey` derives the schema-valid challenge binding. */
|
|
17
|
+
readonly agent: {
|
|
18
|
+
readonly did: string;
|
|
19
|
+
readonly privateKey: Uint8Array;
|
|
20
|
+
readonly publicKey: Uint8Array;
|
|
21
|
+
};
|
|
22
|
+
/** The OPERATOR identity — the SEPARATE human key. */
|
|
23
|
+
readonly operator: {
|
|
24
|
+
readonly did: string;
|
|
25
|
+
readonly privateKey: Uint8Array;
|
|
26
|
+
readonly publicKey: Uint8Array;
|
|
27
|
+
};
|
|
28
|
+
readonly ttlMs?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build the two-key `SignedActionEnvelope` that authorizes a resume of a
|
|
32
|
+
* restricted tool. The two signature `kid`s DIFFER by construction (agent vs
|
|
33
|
+
* operator), so forging human judgment would require the operator private key.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildHumanApprovalEnvelope(input: BuildHumanApprovalEnvelopeInput): Promise<SignedActionEnvelope>;
|
|
36
|
+
export interface AppendOperatorSignoffInput {
|
|
37
|
+
readonly forensicLedger: ForensicLedgerService;
|
|
38
|
+
readonly ledgerStore: LedgerStore;
|
|
39
|
+
readonly sessionId: string;
|
|
40
|
+
readonly operator: {
|
|
41
|
+
readonly did: string;
|
|
42
|
+
readonly privateKey: Uint8Array;
|
|
43
|
+
};
|
|
44
|
+
readonly escalation: {
|
|
45
|
+
readonly suspendedStepId: string;
|
|
46
|
+
readonly toolName: string;
|
|
47
|
+
readonly escalationId?: string;
|
|
48
|
+
};
|
|
49
|
+
/** MANDATORY classification (required `failureClass`, no "unclassified"). */
|
|
50
|
+
readonly classification: ExceptionClassificationInput;
|
|
51
|
+
readonly exceptionClassificationStore: ExceptionClassificationStore;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Append a SIGNED ledger row recording the operator's sign-off (OPERATOR key,
|
|
55
|
+
* actorClass OPERATOR_ADMIN) + persist the signed exception classification.
|
|
56
|
+
* The mandatory `failureClass` gate fails-fast before any row is written.
|
|
57
|
+
*/
|
|
58
|
+
export declare function appendOperatorSignoff(input: AppendOperatorSignoffInput): Promise<{
|
|
59
|
+
stepIndex: number;
|
|
60
|
+
classification: ExceptionClassificationRecord;
|
|
61
|
+
}>;
|
|
62
|
+
export interface VerifyApprovalEnvelopeInput {
|
|
63
|
+
readonly envelope: SignedActionEnvelope;
|
|
64
|
+
/** The AGENT public key (the host's own key) that attested the challenge. */
|
|
65
|
+
readonly agentPublicKey: Uint8Array;
|
|
66
|
+
/** The OPERATOR public key — the SEPARATE human key the host cannot mint. */
|
|
67
|
+
readonly operatorPublicKey: Uint8Array;
|
|
68
|
+
/** If given, the envelope's proposed tool MUST equal this (bind to the suspended call). */
|
|
69
|
+
readonly expectedToolName?: string;
|
|
70
|
+
/** If given, the challenge must not be expired at this wall-clock (ms since epoch). */
|
|
71
|
+
readonly nowMs?: number;
|
|
72
|
+
}
|
|
73
|
+
export type ApprovalVerificationReason = "MISSING_SIGNATURE_KID" | "KEYS_NOT_DISTINCT" | "TOOL_MISMATCH" | "WRONG_DST" | "EXPIRED" | "AGENT_ATTESTATION_INVALID" | "OPERATOR_SIGNATURE_INVALID";
|
|
74
|
+
export interface ApprovalVerification {
|
|
75
|
+
readonly ok: boolean;
|
|
76
|
+
readonly reason: ApprovalVerificationReason | null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* CRYPTOGRAPHICALLY verify a two-key human-approval envelope before a suspended
|
|
80
|
+
* run may resume (audit r1 F1). The security-critical check is the OPERATOR
|
|
81
|
+
* (invoker) signature over `stripDerivedFields(challenge)` under a key the host
|
|
82
|
+
* does NOT hold — so the host cannot forge human judgment by merely PRESENTING an
|
|
83
|
+
* envelope. Also re-checks: the AGENT attestation, the two kids differ, the DST is
|
|
84
|
+
* the handshake DST, the proposed tool matches, and (optionally) the challenge
|
|
85
|
+
* has not expired. Returns `{ok:false, reason}` on any failure — never throws.
|
|
86
|
+
*/
|
|
87
|
+
export declare function verifyApprovalEnvelope(input: VerifyApprovalEnvelopeInput): Promise<ApprovalVerification>;
|
|
88
|
+
/** One-way claim guard — an escalation/resume key may be claimed AT MOST ONCE,
|
|
89
|
+
* so a retried resume can't double-execute a money-moving tool. In-memory
|
|
90
|
+
* (single process); a durable CAS is a venture concern. */
|
|
91
|
+
export declare class ResumeGuard {
|
|
92
|
+
private readonly claimed;
|
|
93
|
+
/** Claim `escalationKey`. Throws `EscalationError("RESUME_ALREADY_CLAIMED")`
|
|
94
|
+
* if already claimed — the throw is the defense. */
|
|
95
|
+
claim(escalationKey: string): void;
|
|
96
|
+
isClaimed(escalationKey: string): boolean;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=escalation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalation.d.ts","sourceRoot":"","sources":["../../src/runtime/escalation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// @kashscript/hudhud/runtime — escalation: two-key suspend/resume
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// When an autonomous run reaches a RESTRICTED tool it SUSPENDS; a human must
|
|
5
|
+
// sign off before it may RESUME. Two artifacts make "the host cannot forge
|
|
6
|
+
// human judgment" literally true:
|
|
7
|
+
// 1. buildHumanApprovalEnvelope — the TWO-KEY envelope: the AGENT key (which
|
|
8
|
+
// the host holds) attests the proposed action; a SEPARATE OPERATOR key
|
|
9
|
+
// signs the challenge. The host cannot mint the invoker signature.
|
|
10
|
+
// 2. appendOperatorSignoff — a SIGNED ledger row on the OPERATOR key
|
|
11
|
+
// (actorClass OPERATOR_ADMIN) + the mandatory exception classification;
|
|
12
|
+
// forensicConsistencyScan re-verifies it.
|
|
13
|
+
// 3. ResumeGuard — a one-way claim() so a retried resume can't re-fire a tool.
|
|
14
|
+
// Ported from the oreoasis-core-host engine (M5); telemetry dropped, ledger +
|
|
15
|
+
// exception + neuron/handshake reused.
|
|
16
|
+
// ============================================================================
|
|
17
|
+
import { deriveDid, signEvent, verifyEvent } from "@kashscript/identity-core";
|
|
18
|
+
import { KASH_DID_PATTERN, signLedgerBlock } from "@kashscript/attest";
|
|
19
|
+
import { PermissionGate, assembleEnvelope, stripDerivedFields, } from "@kashscript/neuron/handshake";
|
|
20
|
+
import { NEURON_DST } from "@kashscript/neuron/schemas";
|
|
21
|
+
import { assertFailureClass, buildSignedClassification, } from "../exception";
|
|
22
|
+
import { resolvePreviousBlockHash } from "../ledger/store";
|
|
23
|
+
export class EscalationError extends Error {
|
|
24
|
+
code;
|
|
25
|
+
detail;
|
|
26
|
+
constructor(code, message, detail = {}) {
|
|
27
|
+
super(`[${code}] ${message}`);
|
|
28
|
+
this.name = "EscalationError";
|
|
29
|
+
this.code = code;
|
|
30
|
+
this.detail = Object.freeze({ ...detail });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Coerce a 64-hex-ish id to the neuron schema's `0x`-prefixed lowercase-hex form. */
|
|
34
|
+
function asSha256Hex(s) {
|
|
35
|
+
const lower = s.toLowerCase();
|
|
36
|
+
const hex = lower.startsWith("0x") ? lower.slice(2) : lower;
|
|
37
|
+
return `0x${hex}`;
|
|
38
|
+
}
|
|
39
|
+
/** Derive the real `did:kash:` binding for a public key (schema-valid DID). */
|
|
40
|
+
function deriveKashDid(publicKey) {
|
|
41
|
+
return deriveDid(publicKey);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build the two-key `SignedActionEnvelope` that authorizes a resume of a
|
|
45
|
+
* restricted tool. The two signature `kid`s DIFFER by construction (agent vs
|
|
46
|
+
* operator), so forging human judgment would require the operator private key.
|
|
47
|
+
*/
|
|
48
|
+
export async function buildHumanApprovalEnvelope(input) {
|
|
49
|
+
if (!KASH_DID_PATTERN.test(input.agent.did)) {
|
|
50
|
+
throw new EscalationError("AGENT_DID_INVALID", "agent.did must be a valid did:kash identifier");
|
|
51
|
+
}
|
|
52
|
+
if (!KASH_DID_PATTERN.test(input.operator.did)) {
|
|
53
|
+
throw new EscalationError("OPERATOR_DID_INVALID", "operator.did must be a valid did:kash identifier");
|
|
54
|
+
}
|
|
55
|
+
if (input.agent.did === input.operator.did) {
|
|
56
|
+
throw new EscalationError("AGENT_OPERATOR_NOT_DISTINCT", "agent.did and operator.did must differ — the two-key guarantee requires a SEPARATE operator key");
|
|
57
|
+
}
|
|
58
|
+
// 1. AGENT attests the challenge body (proves the agent proposed it).
|
|
59
|
+
const attest = async (body) => {
|
|
60
|
+
const signed = await signEvent({}, body, input.agent.privateKey, { dst: NEURON_DST.HANDSHAKE, encoding: "hex", kid: input.agent.did });
|
|
61
|
+
return { alg: "ed25519", sig: signed.sig, dst: NEURON_DST.HANDSHAKE, encoding: "hex", kid: input.agent.did };
|
|
62
|
+
};
|
|
63
|
+
const gate = new PermissionGate();
|
|
64
|
+
const challenge = await gate.buildChallenge(input.pendingCall, {
|
|
65
|
+
agentId: deriveKashDid(input.agent.publicKey),
|
|
66
|
+
invokerId: deriveKashDid(input.operator.publicKey),
|
|
67
|
+
blueprintId: asSha256Hex(input.blueprintId),
|
|
68
|
+
}, attest, { dagId: asSha256Hex(input.dagId), ...(input.ttlMs !== undefined ? { ttlMs: input.ttlMs } : {}) });
|
|
69
|
+
// 2. OPERATOR signs the SAME stripped body with the SEPARATE key.
|
|
70
|
+
const operatorSigned = await signEvent({}, stripDerivedFields(challenge), input.operator.privateKey, {
|
|
71
|
+
dst: NEURON_DST.HANDSHAKE,
|
|
72
|
+
encoding: "hex",
|
|
73
|
+
kid: input.operator.did,
|
|
74
|
+
});
|
|
75
|
+
const invokerSignature = {
|
|
76
|
+
alg: "ed25519",
|
|
77
|
+
sig: operatorSigned.sig,
|
|
78
|
+
dst: NEURON_DST.HANDSHAKE,
|
|
79
|
+
encoding: "hex",
|
|
80
|
+
kid: input.operator.did,
|
|
81
|
+
};
|
|
82
|
+
return assembleEnvelope(challenge, invokerSignature);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Append a SIGNED ledger row recording the operator's sign-off (OPERATOR key,
|
|
86
|
+
* actorClass OPERATOR_ADMIN) + persist the signed exception classification.
|
|
87
|
+
* The mandatory `failureClass` gate fails-fast before any row is written.
|
|
88
|
+
*/
|
|
89
|
+
export async function appendOperatorSignoff(input) {
|
|
90
|
+
if (!KASH_DID_PATTERN.test(input.operator.did)) {
|
|
91
|
+
throw new EscalationError("OPERATOR_DID_INVALID", "operator.did must be a valid did:kash identifier");
|
|
92
|
+
}
|
|
93
|
+
assertFailureClass(input.classification.failureClass);
|
|
94
|
+
const payloadMutation = {
|
|
95
|
+
event: "escalation.signoff",
|
|
96
|
+
suspendedStepId: input.escalation.suspendedStepId,
|
|
97
|
+
tool: input.escalation.toolName,
|
|
98
|
+
failureClass: input.classification.failureClass,
|
|
99
|
+
failureTag: input.classification.failureTag,
|
|
100
|
+
};
|
|
101
|
+
const { previousBlockHash, nextStepIndex } = await resolvePreviousBlockHash(input.ledgerStore, input.sessionId);
|
|
102
|
+
const signature = await signLedgerBlock({ sessionId: input.sessionId, stepIndex: nextStepIndex, actorDid: input.operator.did, previousBlockHash, payloadMutation }, input.operator.privateKey);
|
|
103
|
+
const block = await input.forensicLedger.appendTransitionLog({
|
|
104
|
+
sessionId: input.sessionId,
|
|
105
|
+
stepIndex: nextStepIndex,
|
|
106
|
+
actorDid: input.operator.did,
|
|
107
|
+
actorClass: "OPERATOR_ADMIN",
|
|
108
|
+
payloadMutation,
|
|
109
|
+
signature,
|
|
110
|
+
});
|
|
111
|
+
const classification = await buildSignedClassification(input.classification, input.operator, {
|
|
112
|
+
escalationId: input.escalation.escalationId ?? null,
|
|
113
|
+
signoffLedgerStepIndex: block.stepIndex,
|
|
114
|
+
});
|
|
115
|
+
await input.exceptionClassificationStore.append(classification);
|
|
116
|
+
return { stepIndex: block.stepIndex, classification };
|
|
117
|
+
}
|
|
118
|
+
function toSignedEvent(body, sig) {
|
|
119
|
+
// The envelope's signatures were minted with `encoding: "hex"` + a `kid`; the
|
|
120
|
+
// neuron type marks both optional, so default them for identity-core's verifier.
|
|
121
|
+
return {
|
|
122
|
+
header: {},
|
|
123
|
+
body,
|
|
124
|
+
signature: { alg: sig.alg, sig: sig.sig, dst: sig.dst, encoding: sig.encoding ?? "hex", kid: sig.kid ?? "" },
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* CRYPTOGRAPHICALLY verify a two-key human-approval envelope before a suspended
|
|
129
|
+
* run may resume (audit r1 F1). The security-critical check is the OPERATOR
|
|
130
|
+
* (invoker) signature over `stripDerivedFields(challenge)` under a key the host
|
|
131
|
+
* does NOT hold — so the host cannot forge human judgment by merely PRESENTING an
|
|
132
|
+
* envelope. Also re-checks: the AGENT attestation, the two kids differ, the DST is
|
|
133
|
+
* the handshake DST, the proposed tool matches, and (optionally) the challenge
|
|
134
|
+
* has not expired. Returns `{ok:false, reason}` on any failure — never throws.
|
|
135
|
+
*/
|
|
136
|
+
export async function verifyApprovalEnvelope(input) {
|
|
137
|
+
const { envelope, agentPublicKey, operatorPublicKey } = input;
|
|
138
|
+
const challenge = envelope.challenge;
|
|
139
|
+
const agentAttestation = challenge.agentAttestation;
|
|
140
|
+
const invokerSignature = envelope.invokerSignature;
|
|
141
|
+
const agentKid = agentAttestation?.kid;
|
|
142
|
+
const invokerKid = invokerSignature?.kid;
|
|
143
|
+
if (typeof agentKid !== "string" || typeof invokerKid !== "string")
|
|
144
|
+
return { ok: false, reason: "MISSING_SIGNATURE_KID" };
|
|
145
|
+
// Two-key: the operator key is SEPARATE from the agent key the host holds.
|
|
146
|
+
if (agentKid === invokerKid)
|
|
147
|
+
return { ok: false, reason: "KEYS_NOT_DISTINCT" };
|
|
148
|
+
if (input.expectedToolName !== undefined && challenge.proposedAction.toolName !== input.expectedToolName) {
|
|
149
|
+
return { ok: false, reason: "TOOL_MISMATCH" };
|
|
150
|
+
}
|
|
151
|
+
if (agentAttestation.dst !== NEURON_DST.HANDSHAKE || invokerSignature.dst !== NEURON_DST.HANDSHAKE) {
|
|
152
|
+
return { ok: false, reason: "WRONG_DST" };
|
|
153
|
+
}
|
|
154
|
+
if (input.nowMs !== undefined) {
|
|
155
|
+
const expiresMs = Date.parse(challenge.expiresAt);
|
|
156
|
+
if (Number.isFinite(expiresMs) && input.nowMs > expiresMs)
|
|
157
|
+
return { ok: false, reason: "EXPIRED" };
|
|
158
|
+
}
|
|
159
|
+
const signable = stripDerivedFields(challenge);
|
|
160
|
+
const agentOk = await verifyEvent(toSignedEvent(signable, agentAttestation), agentPublicKey);
|
|
161
|
+
if (!agentOk)
|
|
162
|
+
return { ok: false, reason: "AGENT_ATTESTATION_INVALID" };
|
|
163
|
+
// SECURITY-CRITICAL: the operator signed the SAME stripped body with the key
|
|
164
|
+
// the host cannot mint. This is what makes "the host cannot forge human
|
|
165
|
+
// judgment" literally true at RESUME time (not just at build time).
|
|
166
|
+
const operatorOk = await verifyEvent(toSignedEvent(signable, invokerSignature), operatorPublicKey);
|
|
167
|
+
if (!operatorOk)
|
|
168
|
+
return { ok: false, reason: "OPERATOR_SIGNATURE_INVALID" };
|
|
169
|
+
return { ok: true, reason: null };
|
|
170
|
+
}
|
|
171
|
+
/** One-way claim guard — an escalation/resume key may be claimed AT MOST ONCE,
|
|
172
|
+
* so a retried resume can't double-execute a money-moving tool. In-memory
|
|
173
|
+
* (single process); a durable CAS is a venture concern. */
|
|
174
|
+
export class ResumeGuard {
|
|
175
|
+
claimed = new Set();
|
|
176
|
+
/** Claim `escalationKey`. Throws `EscalationError("RESUME_ALREADY_CLAIMED")`
|
|
177
|
+
* if already claimed — the throw is the defense. */
|
|
178
|
+
claim(escalationKey) {
|
|
179
|
+
if (this.claimed.has(escalationKey)) {
|
|
180
|
+
throw new EscalationError("RESUME_ALREADY_CLAIMED", `resume key '${escalationKey}' was already claimed — refusing to re-execute`, { escalationKey });
|
|
181
|
+
}
|
|
182
|
+
this.claimed.add(escalationKey);
|
|
183
|
+
}
|
|
184
|
+
isClaimed(escalationKey) {
|
|
185
|
+
return this.claimed.has(escalationKey);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=escalation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalation.js","sourceRoot":"","sources":["../../src/runtime/escalation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./dispatcher";
|
|
2
|
+
export * from "./runner";
|
|
3
|
+
export * from "./escalation";
|
|
4
|
+
export type { ToolManifest, TaskDAG, AgenticSpec } from "@kashscript/neuron/schemas";
|
|
5
|
+
export type { LLMProvider, DispatchInput, DispatchResult, StepOutcome } from "@kashscript/neuron/kernel";
|
|
6
|
+
export type { SignedActionEnvelope, ToolCall } from "@kashscript/neuron/handshake";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAMA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAG7B,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACrF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACzG,YAAY,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @kashscript/hudhud/runtime — the accountable execution runtime: the neuron
|
|
2
|
+
// tool dispatcher (inline signed ledger append per tool call), the work-item
|
|
3
|
+
// runner (owns the neuron DAGExecutor lifecycle), and the two-key escalation
|
|
4
|
+
// mechanic. Ventures import hudhud + define tool bindings; they never touch raw
|
|
5
|
+
// neuron (ECOSYSTEM #12). The SPI types ventures implement are re-exported here.
|
|
6
|
+
export * from "./dispatcher";
|
|
7
|
+
export * from "./runner";
|
|
8
|
+
export * from "./escalation";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,gFAAgF;AAChF,iFAAiF;AAEjF,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { LLMProvider, StepOutcome } from "@kashscript/neuron/kernel";
|
|
2
|
+
import type { SignedActionEnvelope, ToolCall } from "@kashscript/neuron/handshake";
|
|
3
|
+
import type { AgenticSpec, ResourceQuotas, TaskDAG } from "@kashscript/neuron/schemas";
|
|
4
|
+
import type { McpClientPoolManager } from "../mcp";
|
|
5
|
+
import type { ForensicLedgerService } from "../ledger/writer";
|
|
6
|
+
import type { NeuronToolBinding } from "./dispatcher";
|
|
7
|
+
import type { ResumeGuard } from "./escalation";
|
|
8
|
+
export type { NeuronToolBinding } from "./dispatcher";
|
|
9
|
+
/** The three TERMINAL delivery statuses a single executor run can produce. */
|
|
10
|
+
export type RunTerminalStatus = "DELIVERED" | "ESCALATED" | "FAILED";
|
|
11
|
+
export interface RunBudget {
|
|
12
|
+
readonly maxToolCalls?: number;
|
|
13
|
+
readonly maxWallClockMs?: number;
|
|
14
|
+
readonly costCeilingUsdMicros?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface WorkItemRunInput {
|
|
17
|
+
readonly sessionId: string;
|
|
18
|
+
readonly dag: TaskDAG;
|
|
19
|
+
readonly bindings: ReadonlyMap<string, NeuronToolBinding>;
|
|
20
|
+
readonly llm?: LLMProvider;
|
|
21
|
+
readonly budget?: RunBudget;
|
|
22
|
+
}
|
|
23
|
+
export interface WorkItemRunResult {
|
|
24
|
+
readonly status: RunTerminalStatus;
|
|
25
|
+
readonly outcome: Record<string, unknown>;
|
|
26
|
+
readonly escalation?: {
|
|
27
|
+
readonly suspendedStepId: string;
|
|
28
|
+
readonly pendingToolName?: string;
|
|
29
|
+
readonly pendingCall?: ToolCall;
|
|
30
|
+
readonly priorOutputs: ReadonlyMap<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
readonly failure?: {
|
|
33
|
+
readonly failedStepId: string;
|
|
34
|
+
readonly reason: string;
|
|
35
|
+
};
|
|
36
|
+
readonly stepOutcomes: readonly StepOutcome[];
|
|
37
|
+
}
|
|
38
|
+
export interface WorkItemResumeInput {
|
|
39
|
+
readonly sessionId: string;
|
|
40
|
+
readonly dag: TaskDAG;
|
|
41
|
+
readonly bindings: ReadonlyMap<string, NeuronToolBinding>;
|
|
42
|
+
readonly priorOutputs: ReadonlyMap<string, unknown>;
|
|
43
|
+
readonly envelope: SignedActionEnvelope;
|
|
44
|
+
/** The AGENT public key (host's own key) that attested the challenge. */
|
|
45
|
+
readonly agentPublicKey: Uint8Array;
|
|
46
|
+
/** The OPERATOR public key — the SEPARATE human key that signed off. */
|
|
47
|
+
readonly operatorPublicKey: Uint8Array;
|
|
48
|
+
/** Optional one-way replay guard; the resume is claimed under the challenge nonce. */
|
|
49
|
+
readonly resumeGuard?: ResumeGuard;
|
|
50
|
+
/** Optional wall-clock (ms) — when given, an expired challenge is rejected. */
|
|
51
|
+
readonly nowMs?: number;
|
|
52
|
+
readonly llm?: LLMProvider;
|
|
53
|
+
readonly budget?: RunBudget;
|
|
54
|
+
}
|
|
55
|
+
export interface WorkItemRunnerDeps {
|
|
56
|
+
readonly mcpClientPool: McpClientPoolManager;
|
|
57
|
+
readonly forensicLedger: ForensicLedgerService;
|
|
58
|
+
}
|
|
59
|
+
export declare class WorkItemRunner {
|
|
60
|
+
private readonly deps;
|
|
61
|
+
constructor(deps: WorkItemRunnerDeps);
|
|
62
|
+
run(input: WorkItemRunInput): Promise<WorkItemRunResult>;
|
|
63
|
+
/** Resume a previously-ESCALATED run with the human-approved envelope. audit r1
|
|
64
|
+
* F1/F9: the envelope is CRYPTOGRAPHICALLY verified (two-key: operator signature
|
|
65
|
+
* under a key the host cannot mint) and the resume is claimed once under the
|
|
66
|
+
* challenge nonce — a run can never resume on envelope PRESENCE alone. */
|
|
67
|
+
resume(input: WorkItemResumeInput): Promise<WorkItemRunResult>;
|
|
68
|
+
private executeAndMap;
|
|
69
|
+
}
|
|
70
|
+
/** ResourceQuotas the budgeter enforces for one run (generous defaults +
|
|
71
|
+
* caller overrides). Branded-NSID lexicon fields are opaque placeholders the
|
|
72
|
+
* executor never dereferences at runtime; the cast localises the brand erasure. */
|
|
73
|
+
export declare function buildRunnerQuotas(budget?: RunBudget): ResourceQuotas;
|
|
74
|
+
/** The placeholder AgenticSpec the ExecutionContext requires. The executor
|
|
75
|
+
* reads it only through a Critic, which the runtime does not wire — so an
|
|
76
|
+
* identity-only placeholder is correct. Single localised cast. */
|
|
77
|
+
export declare function buildPlaceholderSpec(sessionId: string): AgenticSpec;
|
|
78
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/runtime/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// @kashscript/hudhud/runtime — WorkItemRunner
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// The run-loop driver for ONE work-item's compiled TaskDAG: builds a
|
|
5
|
+
// NeuronToolDispatcher over the session (a signed row per tool dispatch),
|
|
6
|
+
// constructs a neuron DAGExecutor (dispatcher + PermissionGate + optional LLM),
|
|
7
|
+
// runs the DAG, and MAPS the terminal ExecutionResult onto a delivery status
|
|
8
|
+
// (DELIVERED / ESCALATED / FAILED). Pure (DAG in → status out); does not touch
|
|
9
|
+
// persistence. Ported from the oreoasis-core-host engine (M5); telemetry
|
|
10
|
+
// dropped, placeholder identity/lexicon names neutralized.
|
|
11
|
+
// ============================================================================
|
|
12
|
+
import { DAGExecutor, ResourceBudgeter } from "@kashscript/neuron/kernel";
|
|
13
|
+
import { PermissionGate } from "@kashscript/neuron/handshake";
|
|
14
|
+
import { NeuronToolDispatcher } from "./dispatcher";
|
|
15
|
+
import { verifyApprovalEnvelope } from "./escalation";
|
|
16
|
+
export class WorkItemRunner {
|
|
17
|
+
deps;
|
|
18
|
+
constructor(deps) {
|
|
19
|
+
this.deps = deps;
|
|
20
|
+
}
|
|
21
|
+
async run(input) {
|
|
22
|
+
return this.executeAndMap({
|
|
23
|
+
sessionId: input.sessionId,
|
|
24
|
+
dag: input.dag,
|
|
25
|
+
bindings: input.bindings,
|
|
26
|
+
...(input.llm ? { llm: input.llm } : {}),
|
|
27
|
+
...(input.budget ? { budget: input.budget } : {}),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Resume a previously-ESCALATED run with the human-approved envelope. audit r1
|
|
31
|
+
* F1/F9: the envelope is CRYPTOGRAPHICALLY verified (two-key: operator signature
|
|
32
|
+
* under a key the host cannot mint) and the resume is claimed once under the
|
|
33
|
+
* challenge nonce — a run can never resume on envelope PRESENCE alone. */
|
|
34
|
+
async resume(input) {
|
|
35
|
+
const toolName = input.envelope.challenge.proposedAction.toolName;
|
|
36
|
+
const verdict = await verifyApprovalEnvelope({
|
|
37
|
+
envelope: input.envelope,
|
|
38
|
+
agentPublicKey: input.agentPublicKey,
|
|
39
|
+
operatorPublicKey: input.operatorPublicKey,
|
|
40
|
+
expectedToolName: toolName,
|
|
41
|
+
...(input.nowMs !== undefined ? { nowMs: input.nowMs } : {}),
|
|
42
|
+
});
|
|
43
|
+
if (!verdict.ok) {
|
|
44
|
+
return { status: "FAILED", outcome: {}, failure: { failedStepId: "<resume>", reason: `approval-envelope invalid: ${verdict.reason}` }, stepOutcomes: [] };
|
|
45
|
+
}
|
|
46
|
+
// F9: one-way claim keyed by the challenge nonce so a retried/at-least-once
|
|
47
|
+
// resume can't re-fire the restricted tool.
|
|
48
|
+
if (input.resumeGuard !== undefined) {
|
|
49
|
+
try {
|
|
50
|
+
input.resumeGuard.claim(input.envelope.challenge.nonce);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { status: "FAILED", outcome: {}, failure: { failedStepId: "<resume>", reason: "resume already claimed (replay refused)" }, stepOutcomes: [] };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return this.executeAndMap({
|
|
57
|
+
sessionId: input.sessionId,
|
|
58
|
+
dag: input.dag,
|
|
59
|
+
bindings: input.bindings,
|
|
60
|
+
priorOutputs: input.priorOutputs,
|
|
61
|
+
envelopesByToolName: new Map([[toolName, input.envelope]]),
|
|
62
|
+
...(input.llm ? { llm: input.llm } : {}),
|
|
63
|
+
...(input.budget ? { budget: input.budget } : {}),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async executeAndMap(args) {
|
|
67
|
+
const toolDispatcher = new NeuronToolDispatcher({
|
|
68
|
+
mcpClientPool: this.deps.mcpClientPool,
|
|
69
|
+
forensicLedger: this.deps.forensicLedger,
|
|
70
|
+
sessionId: args.sessionId,
|
|
71
|
+
bindings: args.bindings,
|
|
72
|
+
});
|
|
73
|
+
// onEvent is telemetry only — the signed rows land awaited in the dispatcher.
|
|
74
|
+
const onEvent = (_e) => { };
|
|
75
|
+
const executor = new DAGExecutor({
|
|
76
|
+
toolDispatcher,
|
|
77
|
+
permissionGate: new PermissionGate(),
|
|
78
|
+
...(args.llm ? { llm: args.llm } : {}),
|
|
79
|
+
onEvent,
|
|
80
|
+
});
|
|
81
|
+
const priorOutputs = new Map();
|
|
82
|
+
const ctx = {
|
|
83
|
+
spec: buildPlaceholderSpec(args.sessionId),
|
|
84
|
+
budgeter: new ResourceBudgeter(buildRunnerQuotas(args.budget)),
|
|
85
|
+
onNodeOutput: (stepId, output) => {
|
|
86
|
+
priorOutputs.set(stepId, output);
|
|
87
|
+
},
|
|
88
|
+
...(args.priorOutputs ? { priorOutputs: args.priorOutputs } : {}),
|
|
89
|
+
...(args.envelopesByToolName ? { envelopesByToolName: args.envelopesByToolName } : {}),
|
|
90
|
+
};
|
|
91
|
+
const result = await executor.execute(args.dag, ctx);
|
|
92
|
+
switch (result.kind) {
|
|
93
|
+
case "completed":
|
|
94
|
+
return { status: "DELIVERED", outcome: Object.fromEntries(result.outputs), stepOutcomes: result.outcomes };
|
|
95
|
+
case "suspended": {
|
|
96
|
+
if (result.reason === "awaiting-signature") {
|
|
97
|
+
return {
|
|
98
|
+
status: "ESCALATED",
|
|
99
|
+
outcome: {},
|
|
100
|
+
escalation: {
|
|
101
|
+
suspendedStepId: result.stepId,
|
|
102
|
+
...(result.pendingCall ? { pendingCall: result.pendingCall, pendingToolName: result.pendingCall.toolName } : {}),
|
|
103
|
+
priorOutputs,
|
|
104
|
+
},
|
|
105
|
+
stepOutcomes: result.outcomes,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { status: "FAILED", outcome: {}, failure: { failedStepId: result.stepId, reason: `suspended:${result.reason}` }, stepOutcomes: result.outcomes };
|
|
109
|
+
}
|
|
110
|
+
case "failed":
|
|
111
|
+
return { status: "FAILED", outcome: {}, failure: { failedStepId: result.failedStepId, reason: result.reason }, stepOutcomes: result.outcomes };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** ResourceQuotas the budgeter enforces for one run (generous defaults +
|
|
116
|
+
* caller overrides). Branded-NSID lexicon fields are opaque placeholders the
|
|
117
|
+
* executor never dereferences at runtime; the cast localises the brand erasure. */
|
|
118
|
+
export function buildRunnerQuotas(budget) {
|
|
119
|
+
const maxToolCalls = budget?.maxToolCalls ?? 100;
|
|
120
|
+
const maxWallClockMs = budget?.maxWallClockMs ?? 600_000;
|
|
121
|
+
const costCeilingUsdMicros = budget?.costCeilingUsdMicros ?? 1_000_000_000;
|
|
122
|
+
const nsid = (s) => s;
|
|
123
|
+
const quotas = {
|
|
124
|
+
maxStepsPerDAG: 100,
|
|
125
|
+
maxInputTokensPerStep: 100_000,
|
|
126
|
+
maxOutputTokensPerStep: 100_000,
|
|
127
|
+
maxToolCallsPerDAG: maxToolCalls,
|
|
128
|
+
maxWallClockMs,
|
|
129
|
+
judges: { threshold: 2, hallucinationCeiling: 0.5, personaCosine: 0.5, maxJudgeRetries: 1, feedbackSummariseTokens: 256 },
|
|
130
|
+
modelRouter: { costCeilingUsdMicros, latencyCeilingMs: maxWallClockMs, respectGrantTTL: false },
|
|
131
|
+
lexicons: { intent: nsid("kash.run.intent"), thought: nsid("kash.run.thought"), output: nsid("kash.run.output"), extras: [], trustedPublishers: [] },
|
|
132
|
+
compression: { granularity: "moderate", triggerThresholdFraction: 0.2, maxFactArtifactLength: 4000, maxFactArtifacts: 64, preserveEnvelopes: true },
|
|
133
|
+
};
|
|
134
|
+
return quotas;
|
|
135
|
+
}
|
|
136
|
+
/** The placeholder AgenticSpec the ExecutionContext requires. The executor
|
|
137
|
+
* reads it only through a Critic, which the runtime does not wire — so an
|
|
138
|
+
* identity-only placeholder is correct. Single localised cast. */
|
|
139
|
+
export function buildPlaceholderSpec(sessionId) {
|
|
140
|
+
const spec = {
|
|
141
|
+
identity: { invoker: "did:kash:agent:kash-runtime", name: `work-item-runner:${sessionId}`, version: "0.0.0", kind: "kash-neuron-agentic-spec/1" },
|
|
142
|
+
};
|
|
143
|
+
return spec;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=runner.js.map
|