@sentropic/h2a 0.81.0 → 0.83.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/README.md +5 -0
- package/dist/bin-routing.d.ts +13 -0
- package/dist/bin-routing.d.ts.map +1 -0
- package/dist/bin-routing.js +46 -0
- package/dist/bin-routing.js.map +1 -0
- package/dist/bin.js +40 -11
- package/dist/bin.js.map +1 -1
- package/dist/cli-contract.d.ts.map +1 -1
- package/dist/cli-contract.js +22 -6
- package/dist/cli-contract.js.map +1 -1
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +197 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +3 -1
- package/dist/mcp.js.map +1 -1
- package/dist/runtime/canevas/adapter.d.ts +11 -0
- package/dist/runtime/canevas/adapter.d.ts.map +1 -0
- package/dist/runtime/canevas/adapter.js +22 -0
- package/dist/runtime/canevas/adapter.js.map +1 -0
- package/dist/runtime/canevas/aggregate.d.ts +56 -0
- package/dist/runtime/canevas/aggregate.d.ts.map +1 -0
- package/dist/runtime/canevas/aggregate.js +84 -0
- package/dist/runtime/canevas/aggregate.js.map +1 -0
- package/dist/runtime/canevas/answer.d.ts +21 -0
- package/dist/runtime/canevas/answer.d.ts.map +1 -0
- package/dist/runtime/canevas/answer.js +62 -0
- package/dist/runtime/canevas/answer.js.map +1 -0
- package/dist/runtime/canevas/app.d.ts +19 -0
- package/dist/runtime/canevas/app.d.ts.map +1 -0
- package/dist/runtime/canevas/app.js +49 -0
- package/dist/runtime/canevas/app.js.map +1 -0
- package/dist/runtime/canevas/gather.d.ts +3 -0
- package/dist/runtime/canevas/gather.d.ts.map +1 -0
- package/dist/runtime/canevas/gather.js +39 -0
- package/dist/runtime/canevas/gather.js.map +1 -0
- package/dist/runtime/canevas/reply.d.ts +27 -0
- package/dist/runtime/canevas/reply.d.ts.map +1 -0
- package/dist/runtime/canevas/reply.js +31 -0
- package/dist/runtime/canevas/reply.js.map +1 -0
- package/dist/runtime/canevas/serve.d.ts +11 -0
- package/dist/runtime/canevas/serve.d.ts.map +1 -0
- package/dist/runtime/canevas/serve.js +42 -0
- package/dist/runtime/canevas/serve.js.map +1 -0
- package/dist/runtime/canevas/ui.d.ts +2 -0
- package/dist/runtime/canevas/ui.d.ts.map +1 -0
- package/dist/runtime/canevas/ui.js +124 -0
- package/dist/runtime/canevas/ui.js.map +1 -0
- package/dist/runtime/loop/engine/adapters.d.ts +91 -0
- package/dist/runtime/loop/engine/adapters.d.ts.map +1 -0
- package/dist/runtime/loop/engine/adapters.js +292 -0
- package/dist/runtime/loop/engine/adapters.js.map +1 -0
- package/dist/runtime/loop/engine/decision.d.ts +71 -0
- package/dist/runtime/loop/engine/decision.d.ts.map +1 -0
- package/dist/runtime/loop/engine/decision.js +141 -0
- package/dist/runtime/loop/engine/decision.js.map +1 -0
- package/dist/runtime/loop/engine/execute.d.ts +33 -0
- package/dist/runtime/loop/engine/execute.d.ts.map +1 -0
- package/dist/runtime/loop/engine/execute.js +69 -0
- package/dist/runtime/loop/engine/execute.js.map +1 -0
- package/dist/runtime/loop/engine/tick.d.ts +27 -0
- package/dist/runtime/loop/engine/tick.d.ts.map +1 -0
- package/dist/runtime/loop/engine/tick.js +72 -0
- package/dist/runtime/loop/engine/tick.js.map +1 -0
- package/dist/runtime/loop/index.d.ts +14 -0
- package/dist/runtime/loop/index.d.ts.map +1 -1
- package/dist/runtime/loop/index.js +23 -0
- package/dist/runtime/loop/index.js.map +1 -1
- package/dist/runtime/mcp/handlers.d.ts +12 -0
- package/dist/runtime/mcp/handlers.d.ts.map +1 -1
- package/dist/runtime/mcp/handlers.js +42 -0
- package/dist/runtime/mcp/handlers.js.map +1 -1
- package/dist/runtime/mcp/server.d.ts.map +1 -1
- package/dist/runtime/mcp/server.js +5 -1
- package/dist/runtime/mcp/server.js.map +1 -1
- package/dist/runtime/mcp/tools.d.ts.map +1 -1
- package/dist/runtime/mcp/tools.js +22 -0
- package/dist/runtime/mcp/tools.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -106,6 +106,6 @@ export declare const H2A_CLI_ADAPTER: {
|
|
|
106
106
|
readonly corePackageName: "@sentropic/h2a";
|
|
107
107
|
readonly protocol: "sentropic.h2a";
|
|
108
108
|
readonly hosts: readonly [import("./hosts/codex.js").H2AConfigurableHostDescriptor, import("./hosts/codex.js").H2AConfigurableHostDescriptor, import("./hosts/codex.js").H2AConfigurableHostDescriptor, import("./hosts/codex.js").H2AConfigurableHostDescriptor];
|
|
109
|
-
readonly mcpToolNames: readonly ["h2a_register_instance", "h2a_discover_instances", "h2a_open_negotiation", "h2a_offer", "h2a_counteroffer", "h2a_sign", "h2a_stabilize", "h2a_attest_comprehension", "h2a_declare_conflit_interet", "h2a_conflict_posture", "h2a_inbox", "h2a_append_journal", "h2a_escalate", "h2a_session_open", "h2a_session_close", "h2a_discover_sessions", "h2a_nhi_report", "h2a_nhi_inventory", "h2a_nhi_attest", "h2a_nhi_offboard", "h2a_nhi_export", "h2a_blockage_raise", "h2a_blockage_list", "h2a_blockage_resolve", "h2a_conductor", "h2a_conductor_claim", "h2a_conductor_release", "h2a_conductor_launch_check", "h2a_conductor_launch"];
|
|
109
|
+
readonly mcpToolNames: readonly ["h2a_register_instance", "h2a_discover_instances", "h2a_open_negotiation", "h2a_offer", "h2a_counteroffer", "h2a_sign", "h2a_stabilize", "h2a_attest_comprehension", "h2a_declare_conflit_interet", "h2a_conflict_posture", "h2a_inbox", "h2a_append_journal", "h2a_escalate", "h2a_session_open", "h2a_session_close", "h2a_discover_sessions", "h2a_nhi_report", "h2a_nhi_inventory", "h2a_nhi_attest", "h2a_nhi_offboard", "h2a_nhi_export", "h2a_blockage_raise", "h2a_blockage_list", "h2a_blockage_resolve", "h2a_conductor", "h2a_conductor_claim", "h2a_conductor_release", "h2a_conductor_launch_check", "h2a_conductor_launch", "h2a_loop_list", "h2a_loop_status"];
|
|
110
110
|
};
|
|
111
111
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const H2A_CLI_MCP_TOOL_NAMES: readonly ["h2a_register_instance", "h2a_discover_instances", "h2a_open_negotiation", "h2a_offer", "h2a_counteroffer", "h2a_sign", "h2a_stabilize", "h2a_attest_comprehension", "h2a_declare_conflit_interet", "h2a_conflict_posture", "h2a_inbox", "h2a_append_journal", "h2a_escalate", "h2a_session_open", "h2a_session_close", "h2a_discover_sessions", "h2a_nhi_report", "h2a_nhi_inventory", "h2a_nhi_attest", "h2a_nhi_offboard", "h2a_nhi_export", "h2a_blockage_raise", "h2a_blockage_list", "h2a_blockage_resolve", "h2a_conductor", "h2a_conductor_claim", "h2a_conductor_release", "h2a_conductor_launch_check", "h2a_conductor_launch"];
|
|
1
|
+
export declare const H2A_CLI_MCP_TOOL_NAMES: readonly ["h2a_register_instance", "h2a_discover_instances", "h2a_open_negotiation", "h2a_offer", "h2a_counteroffer", "h2a_sign", "h2a_stabilize", "h2a_attest_comprehension", "h2a_declare_conflit_interet", "h2a_conflict_posture", "h2a_inbox", "h2a_append_journal", "h2a_escalate", "h2a_session_open", "h2a_session_close", "h2a_discover_sessions", "h2a_nhi_report", "h2a_nhi_inventory", "h2a_nhi_attest", "h2a_nhi_offboard", "h2a_nhi_export", "h2a_blockage_raise", "h2a_blockage_list", "h2a_blockage_resolve", "h2a_conductor", "h2a_conductor_claim", "h2a_conductor_release", "h2a_conductor_launch_check", "h2a_conductor_launch", "h2a_loop_list", "h2a_loop_status"];
|
|
2
2
|
//# sourceMappingURL=mcp.d.ts.map
|
package/dist/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,ypBAgCzB,CAAC"}
|
package/dist/mcp.js
CHANGED
|
@@ -27,6 +27,8 @@ export const H2A_CLI_MCP_TOOL_NAMES = [
|
|
|
27
27
|
"h2a_conductor_claim",
|
|
28
28
|
"h2a_conductor_release",
|
|
29
29
|
"h2a_conductor_launch_check",
|
|
30
|
-
"h2a_conductor_launch"
|
|
30
|
+
"h2a_conductor_launch",
|
|
31
|
+
"h2a_loop_list",
|
|
32
|
+
"h2a_loop_status"
|
|
31
33
|
];
|
|
32
34
|
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,uBAAuB;IACvB,wBAAwB;IACxB,sBAAsB;IACtB,WAAW;IACX,kBAAkB;IAClB,UAAU;IACV,eAAe;IACf,0BAA0B;IAC1B,6BAA6B;IAC7B,sBAAsB;IACtB,WAAW;IACX,oBAAoB;IACpB,cAAc;IACd,kBAAkB;IAClB,mBAAmB;IACnB,uBAAuB;IACvB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,oBAAoB;IACpB,mBAAmB;IACnB,sBAAsB;IACtB,eAAe;IACf,qBAAqB;IACrB,uBAAuB;IACvB,4BAA4B;IAC5B,sBAAsB;
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,uBAAuB;IACvB,wBAAwB;IACxB,sBAAsB;IACtB,WAAW;IACX,kBAAkB;IAClB,UAAU;IACV,eAAe;IACf,0BAA0B;IAC1B,6BAA6B;IAC7B,sBAAsB;IACtB,WAAW;IACX,oBAAoB;IACpB,cAAc;IACd,kBAAkB;IAClB,mBAAmB;IACnB,uBAAuB;IACvB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,oBAAoB;IACpB,mBAAmB;IACnB,sBAAsB;IACtB,eAAe;IACf,qBAAqB;IACrB,uBAAuB;IACvB,4BAA4B;IAC5B,sBAAsB;IACtB,eAAe;IACf,iBAAiB;CACT,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PaneSnapshot {
|
|
2
|
+
readonly degraded: boolean;
|
|
3
|
+
readonly text: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Read-only tmux pane snapshot for a session's CLI view. Lazily loads the heavy
|
|
7
|
+
* runtime; if it is absent or fails, returns `{degraded:true, text:""}` — never
|
|
8
|
+
* invents output.
|
|
9
|
+
*/
|
|
10
|
+
export declare function capturePaneSnapshot(tmuxName: string, lines?: number): Promise<PaneSnapshot>;
|
|
11
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/adapter.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAW9F"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Canevas ③ — the ONLY file that touches the heavy runtime, via LAZY import
|
|
2
|
+
// (golden rule: @sentropic/h2a never hard-depends on @sentropic/h2a-runtime).
|
|
3
|
+
// Same variable-specifier pattern as loop/engine/adapters.ts + bin.ts. The
|
|
4
|
+
// golden-rule scan forbids a static runtime import here (only the dynamic one).
|
|
5
|
+
/**
|
|
6
|
+
* Read-only tmux pane snapshot for a session's CLI view. Lazily loads the heavy
|
|
7
|
+
* runtime; if it is absent or fails, returns `{degraded:true, text:""}` — never
|
|
8
|
+
* invents output.
|
|
9
|
+
*/
|
|
10
|
+
export async function capturePaneSnapshot(tmuxName, lines = 200) {
|
|
11
|
+
const RUNTIME_PKG = "@sentropic/h2a-runtime";
|
|
12
|
+
try {
|
|
13
|
+
const rt = (await import(RUNTIME_PKG));
|
|
14
|
+
if (typeof rt.capturePaneForH2a !== "function")
|
|
15
|
+
return { degraded: true, text: "" };
|
|
16
|
+
return { degraded: false, text: rt.capturePaneForH2a(tmuxName, lines) };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return { degraded: true, text: "" };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/runtime/canevas/adapter.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAOhF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB,EAAE,KAAK,GAAG,GAAG;IACrE,MAAM,WAAW,GAAW,wBAAwB,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,WAAW,CAAC,CAEpC,CAAC;QACF,IAAI,OAAO,EAAE,CAAC,iBAAiB,KAAK,UAAU;YAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACpF,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type DecisionChannel = "advise" | "decide" | "alert";
|
|
2
|
+
export type DecisionSource = "escalate" | "track" | "loop";
|
|
3
|
+
export interface DecisionSessionRef {
|
|
4
|
+
readonly tmuxName?: string;
|
|
5
|
+
readonly pane?: string;
|
|
6
|
+
readonly sessionId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PendingDecision {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly source: DecisionSource;
|
|
11
|
+
readonly channel?: DecisionChannel;
|
|
12
|
+
readonly instance: string;
|
|
13
|
+
readonly workspace?: string;
|
|
14
|
+
readonly question: string;
|
|
15
|
+
readonly options?: readonly {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly label: string;
|
|
18
|
+
}[];
|
|
19
|
+
readonly sessionRef?: DecisionSessionRef;
|
|
20
|
+
readonly createdAt: string;
|
|
21
|
+
}
|
|
22
|
+
export interface EscalateLikeEnvelope {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly type: string;
|
|
25
|
+
readonly actor?: {
|
|
26
|
+
readonly instance?: string;
|
|
27
|
+
readonly scope?: string;
|
|
28
|
+
};
|
|
29
|
+
readonly body?: unknown;
|
|
30
|
+
readonly createdAt?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AggregateSession {
|
|
33
|
+
readonly instance?: string;
|
|
34
|
+
readonly sessionId?: string;
|
|
35
|
+
readonly workspaceId?: string;
|
|
36
|
+
readonly launchContext?: {
|
|
37
|
+
readonly tmux?: {
|
|
38
|
+
readonly session?: string;
|
|
39
|
+
readonly pane?: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export interface AggregateEntry {
|
|
44
|
+
readonly env: EscalateLikeEnvelope;
|
|
45
|
+
readonly session?: AggregateSession;
|
|
46
|
+
}
|
|
47
|
+
/** PURE: map ONE escalate-like envelope to a PendingDecision, or null if it is
|
|
48
|
+
* not a decision-bearing escalate envelope. */
|
|
49
|
+
export declare function escalateToPendingDecision(entry: AggregateEntry): PendingDecision | null;
|
|
50
|
+
/**
|
|
51
|
+
* PURE: aggregate escalate-like entries into an ordered, de-duplicated list of
|
|
52
|
+
* pending decisions. Dedup by `id`. Order = channel priority (alert > decide >
|
|
53
|
+
* advise > none) then `createdAt` ascending (oldest waiting first).
|
|
54
|
+
*/
|
|
55
|
+
export declare function aggregatePendingDecisions(entries: readonly AggregateEntry[]): PendingDecision[];
|
|
56
|
+
//# sourceMappingURL=aggregate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/aggregate.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9E,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE;YAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACpG;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,oBAAoB,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AA4BD;gDACgD;AAChD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,GAAG,IAAI,CAqBvF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,eAAe,EAAE,CAY/F"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Canevas ③ — PURE aggregation of pending human decisions.
|
|
2
|
+
//
|
|
3
|
+
// Zero IO, zero runtime import. The CLI/server shell gathers escalate-like
|
|
4
|
+
// envelopes (+ their live session) and this module maps + dedups + orders them
|
|
5
|
+
// into a stable `PendingDecision` list. Double-consensus (opus-4-8 + codex xhigh,
|
|
6
|
+
// 2026-07-03): read-only tranche-1, source `escalate` first; track/loop are
|
|
7
|
+
// additive adapters; the reply-bridge (write) is a later, gated tranche.
|
|
8
|
+
const CHANNEL_RANK = { alert: 0, decide: 1, advise: 2 };
|
|
9
|
+
function stringifyQuestion(payload) {
|
|
10
|
+
if (typeof payload === "string")
|
|
11
|
+
return payload;
|
|
12
|
+
if (payload && typeof payload === "object") {
|
|
13
|
+
const p = payload;
|
|
14
|
+
if (typeof p.question === "string")
|
|
15
|
+
return p.question;
|
|
16
|
+
if (typeof p.text === "string")
|
|
17
|
+
return p.text;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.stringify(payload);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return "(decision)";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return "(decision)";
|
|
26
|
+
}
|
|
27
|
+
function sessionRefOf(session) {
|
|
28
|
+
if (!session)
|
|
29
|
+
return undefined;
|
|
30
|
+
const ref = {};
|
|
31
|
+
if (session.launchContext?.tmux?.session)
|
|
32
|
+
ref.tmuxName = session.launchContext.tmux.session;
|
|
33
|
+
if (session.launchContext?.tmux?.pane)
|
|
34
|
+
ref.pane = session.launchContext.tmux.pane;
|
|
35
|
+
if (session.sessionId)
|
|
36
|
+
ref.sessionId = session.sessionId;
|
|
37
|
+
return Object.keys(ref).length > 0 ? ref : undefined;
|
|
38
|
+
}
|
|
39
|
+
/** PURE: map ONE escalate-like envelope to a PendingDecision, or null if it is
|
|
40
|
+
* not a decision-bearing escalate envelope. */
|
|
41
|
+
export function escalateToPendingDecision(entry) {
|
|
42
|
+
const { env, session } = entry;
|
|
43
|
+
if (env.type !== "escalate" || typeof env.id !== "string")
|
|
44
|
+
return null;
|
|
45
|
+
const instance = env.actor?.instance ?? session?.instance;
|
|
46
|
+
if (!instance)
|
|
47
|
+
return null;
|
|
48
|
+
const body = env.body;
|
|
49
|
+
const channel = body && typeof body.channel === "string" && body.channel in CHANNEL_RANK
|
|
50
|
+
? body.channel
|
|
51
|
+
: undefined;
|
|
52
|
+
const sessionRef = sessionRefOf(session);
|
|
53
|
+
return {
|
|
54
|
+
id: env.id,
|
|
55
|
+
source: "escalate",
|
|
56
|
+
...(channel !== undefined ? { channel } : {}),
|
|
57
|
+
instance,
|
|
58
|
+
...(session?.workspaceId !== undefined ? { workspace: session.workspaceId } : {}),
|
|
59
|
+
question: stringifyQuestion(body?.payload),
|
|
60
|
+
...(sessionRef !== undefined ? { sessionRef } : {}),
|
|
61
|
+
createdAt: env.createdAt ?? ""
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* PURE: aggregate escalate-like entries into an ordered, de-duplicated list of
|
|
66
|
+
* pending decisions. Dedup by `id`. Order = channel priority (alert > decide >
|
|
67
|
+
* advise > none) then `createdAt` ascending (oldest waiting first).
|
|
68
|
+
*/
|
|
69
|
+
export function aggregatePendingDecisions(entries) {
|
|
70
|
+
const byId = new Map();
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const decision = escalateToPendingDecision(entry);
|
|
73
|
+
if (decision && !byId.has(decision.id))
|
|
74
|
+
byId.set(decision.id, decision);
|
|
75
|
+
}
|
|
76
|
+
const rank = (d) => (d.channel ? CHANNEL_RANK[d.channel] : 3);
|
|
77
|
+
return [...byId.values()].sort((a, b) => {
|
|
78
|
+
const r = rank(a) - rank(b);
|
|
79
|
+
if (r !== 0)
|
|
80
|
+
return r;
|
|
81
|
+
return a.createdAt.localeCompare(b.createdAt);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=aggregate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../../src/runtime/canevas/aggregate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AnswerStatus = "answered" | "already" | "not-found" | "no-live-target" | "invalid";
|
|
2
|
+
export interface AnswerResult {
|
|
3
|
+
readonly status: AnswerStatus;
|
|
4
|
+
readonly decisionId: string;
|
|
5
|
+
readonly answerId?: string;
|
|
6
|
+
readonly envelopeId?: string;
|
|
7
|
+
readonly targetInstance?: string;
|
|
8
|
+
readonly reason?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PostAnswerDeps {
|
|
11
|
+
readonly now: number;
|
|
12
|
+
readonly envelopeId: string;
|
|
13
|
+
}
|
|
14
|
+
/** True if a reply was already recorded for this decision (idempotence). */
|
|
15
|
+
export declare function hasAlreadyAnswered(root: string, decisionId: string): boolean;
|
|
16
|
+
export declare function postDecisionAnswer(root: string, input: {
|
|
17
|
+
decisionId: string;
|
|
18
|
+
answerId: string;
|
|
19
|
+
note?: string;
|
|
20
|
+
}, deps: PostAnswerDeps): AnswerResult;
|
|
21
|
+
//# sourceMappingURL=answer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/answer.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAE/F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AASD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9D,IAAI,EAAE,cAAc,GACnB,YAAY,CA6Cd"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Canevas ③ tranche-3b — IO shell for the reply-bridge. Idempotent (one reply
|
|
2
|
+
// per decision, journalled), resolves the escalating instance LIVE before
|
|
3
|
+
// delivering (RISK #1: never reply to a stale/wrong session), and writes a
|
|
4
|
+
// structured reply-envelope to that instance's inbox.
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { listPresence } from "../local-files/presence.js";
|
|
8
|
+
import { localStorePaths, safePathSegment } from "../local-files/paths.js";
|
|
9
|
+
import { createLocalStore } from "../local-files/store.js";
|
|
10
|
+
import { gatherPendingDecisions } from "./gather.js";
|
|
11
|
+
import { buildDecisionReplyEnvelope } from "./reply.js";
|
|
12
|
+
function repliesDir(root) {
|
|
13
|
+
return join(localStorePaths(root).root, "canevas", "replies");
|
|
14
|
+
}
|
|
15
|
+
function replyFile(root, decisionId) {
|
|
16
|
+
return join(repliesDir(root), `${safePathSegment(decisionId)}.json`);
|
|
17
|
+
}
|
|
18
|
+
/** True if a reply was already recorded for this decision (idempotence). */
|
|
19
|
+
export function hasAlreadyAnswered(root, decisionId) {
|
|
20
|
+
return existsSync(replyFile(root, decisionId));
|
|
21
|
+
}
|
|
22
|
+
export function postDecisionAnswer(root, input, deps) {
|
|
23
|
+
const { decisionId, answerId } = input;
|
|
24
|
+
if (!decisionId || !answerId) {
|
|
25
|
+
return { status: "invalid", decisionId, reason: "decisionId and answerId are required" };
|
|
26
|
+
}
|
|
27
|
+
// Idempotence — one reply per decision.
|
|
28
|
+
if (hasAlreadyAnswered(root, decisionId)) {
|
|
29
|
+
return { status: "already", decisionId, reason: "decision already answered" };
|
|
30
|
+
}
|
|
31
|
+
// The decision must still be pending.
|
|
32
|
+
const decision = gatherPendingDecisions(root).find((d) => d.id === decisionId);
|
|
33
|
+
if (!decision)
|
|
34
|
+
return { status: "not-found", decisionId, reason: "decision is not pending" };
|
|
35
|
+
// Resolve the escalating instance LIVE (RISK #1): never deliver to a stale
|
|
36
|
+
// instance — the answer could land in the wrong session.
|
|
37
|
+
const live = listPresence(root).some((s) => s.instance === decision.instance);
|
|
38
|
+
if (!live) {
|
|
39
|
+
return { status: "no-live-target", decisionId, reason: `escalating instance ${decision.instance} is not live` };
|
|
40
|
+
}
|
|
41
|
+
const at = new Date(deps.now).toISOString();
|
|
42
|
+
const envelope = buildDecisionReplyEnvelope({
|
|
43
|
+
decisionId,
|
|
44
|
+
answerId,
|
|
45
|
+
...(input.note !== undefined ? { note: input.note } : {}),
|
|
46
|
+
targetInstance: decision.instance,
|
|
47
|
+
envelopeId: deps.envelopeId,
|
|
48
|
+
createdAt: at
|
|
49
|
+
});
|
|
50
|
+
createLocalStore({ root }).putInboxMessage(decision.instance, envelope);
|
|
51
|
+
const record = {
|
|
52
|
+
decisionId,
|
|
53
|
+
answerId,
|
|
54
|
+
envelopeId: deps.envelopeId,
|
|
55
|
+
targetInstance: decision.instance,
|
|
56
|
+
createdAt: at
|
|
57
|
+
};
|
|
58
|
+
mkdirSync(repliesDir(root), { recursive: true });
|
|
59
|
+
writeFileSync(replyFile(root, decisionId), `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
60
|
+
return { status: "answered", decisionId, answerId, envelopeId: deps.envelopeId, targetInstance: decision.instance };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=answer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer.js","sourceRoot":"","sources":["../../../src/runtime/canevas/answer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { PendingDecision } from "./aggregate.js";
|
|
3
|
+
import type { PaneSnapshot } from "./adapter.js";
|
|
4
|
+
import type { AnswerResult } from "./answer.js";
|
|
5
|
+
export interface CanevasDeps {
|
|
6
|
+
/** IO: current pending human decisions (read-only aggregate). */
|
|
7
|
+
readonly listDecisions: () => PendingDecision[];
|
|
8
|
+
/** IO: read-only tmux pane snapshot (lazy runtime; degraded-clean). */
|
|
9
|
+
readonly capturePane: (tmuxName: string, lines: number) => Promise<PaneSnapshot>;
|
|
10
|
+
/** tranche-3b write-bridge: required token for POST /answer. Absent → read-only. */
|
|
11
|
+
readonly writeToken?: string;
|
|
12
|
+
/** tranche-3b write-bridge: gated reply handler. Absent → 501. */
|
|
13
|
+
readonly postAnswer?: (decisionId: string, body: {
|
|
14
|
+
answerId: string;
|
|
15
|
+
note?: string;
|
|
16
|
+
}) => AnswerResult | Promise<AnswerResult>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createCanevasApp(deps: CanevasDeps): Hono;
|
|
19
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/app.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,aAAa,EAAE,MAAM,eAAe,EAAE,CAAC;IAChD,uEAAuE;IACvE,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACjF,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,UAAU,CAAC,EAAE,CACpB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACtC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC3C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CA+CxD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Canevas ③ — read-only Hono app. Deps are injected so the app is testable with
|
|
2
|
+
// fakes; the real wiring (gather + lazy capturePane) lives in serve.ts. NO write
|
|
3
|
+
// routes here (the reply-bridge is a later, gated tranche). Bind 127.0.0.1 only.
|
|
4
|
+
import { Hono } from "hono";
|
|
5
|
+
import { CANEVAS_HTML } from "./ui.js";
|
|
6
|
+
export function createCanevasApp(deps) {
|
|
7
|
+
const app = new Hono();
|
|
8
|
+
// Self-hosted UI. The write token (if any) is injected into the same-origin
|
|
9
|
+
// page so its buttons can POST; a cross-origin page has neither the token nor
|
|
10
|
+
// CORS access.
|
|
11
|
+
app.get("/", (c) => c.html(CANEVAS_HTML.replace("__CANEVAS_TOKEN__", deps.writeToken ?? "")));
|
|
12
|
+
app.get("/api/decisions", (c) => c.json({ kind: "canevas-decisions", version: 1, decisions: deps.listDecisions() }));
|
|
13
|
+
app.get("/api/sessions/:tmuxName/pane", async (c) => {
|
|
14
|
+
const tmuxName = c.req.param("tmuxName");
|
|
15
|
+
const raw = Number(c.req.query("lines") ?? "200");
|
|
16
|
+
const lines = Number.isFinite(raw) && raw > 0 ? Math.min(raw, 2000) : 200;
|
|
17
|
+
const snap = await deps.capturePane(tmuxName, lines);
|
|
18
|
+
return c.json({ kind: "canevas-pane", version: 1, tmuxName, lines, ...snap });
|
|
19
|
+
});
|
|
20
|
+
// tranche-3b — GATED reply-bridge. Requires the write token (same-origin page
|
|
21
|
+
// has it; CORS is closed so no cross-origin write). The handler resolves the
|
|
22
|
+
// target LIVE + is idempotent. answeredBy is "local-human" (never a forged
|
|
23
|
+
// signed attestation).
|
|
24
|
+
app.post("/api/decisions/:id/answer", async (c) => {
|
|
25
|
+
if (!deps.postAnswer)
|
|
26
|
+
return c.json({ error: "reply-bridge disabled" }, 501);
|
|
27
|
+
if (deps.writeToken) {
|
|
28
|
+
const tok = c.req.header("x-canevas-token");
|
|
29
|
+
if (tok !== deps.writeToken)
|
|
30
|
+
return c.json({ error: "forbidden: missing/invalid token" }, 403);
|
|
31
|
+
}
|
|
32
|
+
const id = c.req.param("id");
|
|
33
|
+
const body = (await c.req.json().catch(() => ({})));
|
|
34
|
+
const answerId = typeof body.answerId === "string" ? body.answerId : "";
|
|
35
|
+
if (!answerId)
|
|
36
|
+
return c.json({ error: "answerId (string) is required" }, 400);
|
|
37
|
+
const result = await deps.postAnswer(id, {
|
|
38
|
+
answerId,
|
|
39
|
+
...(typeof body.note === "string" ? { note: body.note } : {})
|
|
40
|
+
});
|
|
41
|
+
const code = result.status === "answered" ? 200 :
|
|
42
|
+
result.status === "already" ? 409 :
|
|
43
|
+
result.status === "not-found" ? 404 :
|
|
44
|
+
result.status === "no-live-target" ? 409 : 400;
|
|
45
|
+
return c.json(result, code);
|
|
46
|
+
});
|
|
47
|
+
return app;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../src/runtime/canevas/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gather.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/gather.ts"],"names":[],"mappings":"AAOA,OAAO,EAAkD,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtG,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CA0BtE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Canevas ③ — IO gatherer (read-only). Collects escalate envelopes across the
|
|
2
|
+
// live instances' inboxes and returns the ordered, de-duplicated pending-decision
|
|
3
|
+
// list. Shared by `h2a canevas list` and the canevas server. No runtime import,
|
|
4
|
+
// no write.
|
|
5
|
+
import { listPresence } from "../local-files/presence.js";
|
|
6
|
+
import { createLocalStore } from "../local-files/store.js";
|
|
7
|
+
import { aggregatePendingDecisions } from "./aggregate.js";
|
|
8
|
+
export function gatherPendingDecisions(root) {
|
|
9
|
+
const store = createLocalStore({ root });
|
|
10
|
+
const sessions = listPresence(root);
|
|
11
|
+
const byInstance = new Map();
|
|
12
|
+
for (const s of sessions) {
|
|
13
|
+
if (s.instance && !byInstance.has(s.instance))
|
|
14
|
+
byInstance.set(s.instance, s);
|
|
15
|
+
}
|
|
16
|
+
const entries = [];
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
for (const s of sessions) {
|
|
19
|
+
if (!s.instance)
|
|
20
|
+
continue;
|
|
21
|
+
let envelopes;
|
|
22
|
+
try {
|
|
23
|
+
envelopes = store.readInbox(s.instance);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
for (const env of envelopes) {
|
|
29
|
+
if (env.type !== "escalate" || seen.has(env.id))
|
|
30
|
+
continue;
|
|
31
|
+
seen.add(env.id);
|
|
32
|
+
const actorInstance = env.actor?.instance;
|
|
33
|
+
const session = actorInstance ? byInstance.get(actorInstance) : undefined;
|
|
34
|
+
entries.push({ env, ...(session ? { session } : {}) });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return aggregatePendingDecisions(entries);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=gather.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gather.js","sourceRoot":"","sources":["../../../src/runtime/canevas/gather.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kFAAkF;AAClF,gFAAgF;AAChF,YAAY;AAEZ,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAA6C,MAAM,gBAAgB,CAAC;AAEtG,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,KAAK,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAqC,CAAC;IAChE,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,CAAC,QAAQ;YAAE,SAAS;QAC1B,IAAI,SAAS,CAAC;QACd,IAAI,CAAC;YACH,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YAC1D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACjB,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC;YAC1C,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1E,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IACD,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { H2AEnvelope } from "../../types.js";
|
|
2
|
+
/** Synthetic actor for canevas-relayed human answers (not a real h2a instance). */
|
|
3
|
+
export declare const CANEVAS_SELF_INSTANCE = "canevas:local-human";
|
|
4
|
+
export interface DecisionReplyInput {
|
|
5
|
+
readonly decisionId: string;
|
|
6
|
+
readonly answerId: string;
|
|
7
|
+
readonly note?: string;
|
|
8
|
+
/** The escalating agent — LIVE-resolved by the shell before calling this. */
|
|
9
|
+
readonly targetInstance: string;
|
|
10
|
+
readonly envelopeId: string;
|
|
11
|
+
readonly createdAt: string;
|
|
12
|
+
readonly scope?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* PURE: build the reply envelope for a human decision answer. Type `event`, body
|
|
16
|
+
* a `decision-reply` message carrying `replyTo`, `answerId` and the transparent
|
|
17
|
+
* `answeredBy:"local-human"` marker.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildDecisionReplyEnvelope(input: DecisionReplyInput): H2AEnvelope;
|
|
20
|
+
export interface ReplyRecord {
|
|
21
|
+
readonly decisionId: string;
|
|
22
|
+
readonly answerId: string;
|
|
23
|
+
readonly envelopeId: string;
|
|
24
|
+
readonly targetInstance: string;
|
|
25
|
+
readonly createdAt: string;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=reply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reply.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/reply.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,kBAAkB,GAAG,WAAW,CAgBjF;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Canevas ③ tranche-3b — PURE reply-envelope construction. The IO (put + journal
|
|
2
|
+
// + live-instance resolution) lives in answer.ts. RISK #1 (consensus): this speaks
|
|
3
|
+
// for the human — `answeredBy:"local-human"` is a transparent marker, NEVER a
|
|
4
|
+
// forged signed h2a attestation. The target is resolved LIVE by the shell to avoid
|
|
5
|
+
// misrouting (multi-session addressing bug).
|
|
6
|
+
import { createEnvelope } from "../../envelope.js";
|
|
7
|
+
/** Synthetic actor for canevas-relayed human answers (not a real h2a instance). */
|
|
8
|
+
export const CANEVAS_SELF_INSTANCE = "canevas:local-human";
|
|
9
|
+
/**
|
|
10
|
+
* PURE: build the reply envelope for a human decision answer. Type `event`, body
|
|
11
|
+
* a `decision-reply` message carrying `replyTo`, `answerId` and the transparent
|
|
12
|
+
* `answeredBy:"local-human"` marker.
|
|
13
|
+
*/
|
|
14
|
+
export function buildDecisionReplyEnvelope(input) {
|
|
15
|
+
return createEnvelope({
|
|
16
|
+
id: input.envelopeId,
|
|
17
|
+
type: "event",
|
|
18
|
+
actor: { instance: CANEVAS_SELF_INSTANCE, role: "PRINCIPAL", scope: input.scope ?? "scope:default" },
|
|
19
|
+
target: { instance: input.targetInstance },
|
|
20
|
+
body: {
|
|
21
|
+
kind: "message",
|
|
22
|
+
topic: "decision-reply",
|
|
23
|
+
replyTo: input.decisionId,
|
|
24
|
+
answerId: input.answerId,
|
|
25
|
+
answeredBy: "local-human",
|
|
26
|
+
...(input.note !== undefined ? { note: input.note } : {})
|
|
27
|
+
},
|
|
28
|
+
createdAt: input.createdAt
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=reply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reply.js","sourceRoot":"","sources":["../../../src/runtime/canevas/reply.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,mFAAmF;AACnF,8EAA8E;AAC9E,mFAAmF;AACnF,6CAA6C;AAE7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AAa3D;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAyB;IAClE,OAAO,cAAc,CAAC;QACpB,EAAE,EAAE,KAAK,CAAC,UAAU;QACpB,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,EAAE,QAAQ,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,eAAe,EAAE;QACpG,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,EAAE;QAC1C,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE,KAAK,CAAC,UAAU;YACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,UAAU,EAAE,aAAa;YACzB,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1D;QACD,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const CANEVAS_DEFAULT_PORT = 8788;
|
|
2
|
+
export interface RunCanevasServeOptions {
|
|
3
|
+
readonly root: string;
|
|
4
|
+
readonly port?: number;
|
|
5
|
+
readonly stderr: {
|
|
6
|
+
write(s: string): void;
|
|
7
|
+
};
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export declare function runCanevasServe(opts: RunCanevasServeOptions): Promise<number>;
|
|
11
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/serve.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCnF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Canevas ③ — server bootstrap. Binds the read-only Hono app to 127.0.0.1 and
|
|
2
|
+
// runs until an abort signal. Wires the real IO (gather + lazy capturePane).
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { serve } from "@hono/node-server";
|
|
5
|
+
import { capturePaneSnapshot } from "./adapter.js";
|
|
6
|
+
import { postDecisionAnswer } from "./answer.js";
|
|
7
|
+
import { createCanevasApp } from "./app.js";
|
|
8
|
+
import { gatherPendingDecisions } from "./gather.js";
|
|
9
|
+
export const CANEVAS_DEFAULT_PORT = 8788;
|
|
10
|
+
export async function runCanevasServe(opts) {
|
|
11
|
+
const port = opts.port !== undefined && Number.isFinite(opts.port) && opts.port > 0
|
|
12
|
+
? opts.port
|
|
13
|
+
: CANEVAS_DEFAULT_PORT;
|
|
14
|
+
// Per-run write token: gates POST /answer (the human copies it, or the
|
|
15
|
+
// same-origin page carries it). Read stays open on localhost.
|
|
16
|
+
const writeToken = randomUUID();
|
|
17
|
+
const app = createCanevasApp({
|
|
18
|
+
listDecisions: () => gatherPendingDecisions(opts.root),
|
|
19
|
+
capturePane: capturePaneSnapshot,
|
|
20
|
+
writeToken,
|
|
21
|
+
postAnswer: (decisionId, body) => postDecisionAnswer(opts.root, { decisionId, ...body }, { now: Date.now(), envelopeId: `env-canevas-reply-${randomUUID()}` })
|
|
22
|
+
});
|
|
23
|
+
// 127.0.0.1 STRICT — never bind a public interface (local human↔agents surface).
|
|
24
|
+
const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" });
|
|
25
|
+
opts.stderr.write(`h2a canevas serve: on http://127.0.0.1:${port} (root=${opts.root})\n`);
|
|
26
|
+
opts.stderr.write(`h2a canevas serve: write token = ${writeToken}\n`);
|
|
27
|
+
return await new Promise((resolve) => {
|
|
28
|
+
const shutdown = () => {
|
|
29
|
+
try {
|
|
30
|
+
server.close?.();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// best-effort
|
|
34
|
+
}
|
|
35
|
+
resolve(0);
|
|
36
|
+
};
|
|
37
|
+
if (opts.signal?.aborted)
|
|
38
|
+
return shutdown();
|
|
39
|
+
opts.signal?.addEventListener("abort", shutdown, { once: true });
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/runtime/canevas/serve.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAE7E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AASzC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAA4B;IAChE,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC;QACpE,CAAC,CAAC,IAAI,CAAC,IAAI;QACX,CAAC,CAAC,oBAAoB,CAAC;IAE3B,uEAAuE;IACvE,8DAA8D;IAC9D,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC;IAEhC,MAAM,GAAG,GAAG,gBAAgB,CAAC;QAC3B,aAAa,EAAE,GAAG,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;QACtD,WAAW,EAAE,mBAAmB;QAChC,UAAU;QACV,UAAU,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAC/B,kBAAkB,CAChB,IAAI,CAAC,IAAI,EACT,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EACvB,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,qBAAqB,UAAU,EAAE,EAAE,EAAE,CACrE;KACJ,CAAC,CAAC;IAEH,iFAAiF;IACjF,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;IACxE,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,0CAA0C,IAAI,UAAU,IAAI,CAAC,IAAI,KAAK,CACvE,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,UAAU,IAAI,CAAC,CAAC;IAEtE,OAAO,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3C,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC1B,IAAI,CAAC;gBACF,MAAiC,CAAC,KAAK,EAAE,EAAE,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,cAAc;YAChB,CAAC;YACD,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,QAAQ,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC"}
|