@a5c-ai/tasks-adapter 5.1.1-staging.52898ebfc24f

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.
Files changed (202) hide show
  1. package/README.md +125 -0
  2. package/dist/auth/forge-interface.d.ts +67 -0
  3. package/dist/auth/forge-interface.d.ts.map +1 -0
  4. package/dist/auth/forge-interface.js +69 -0
  5. package/dist/auth/github-app.d.ts +64 -0
  6. package/dist/auth/github-app.d.ts.map +1 -0
  7. package/dist/auth/github-app.js +141 -0
  8. package/dist/auth/github-oauth.d.ts +27 -0
  9. package/dist/auth/github-oauth.d.ts.map +1 -0
  10. package/dist/auth/github-oauth.js +89 -0
  11. package/dist/auth/index.d.ts +8 -0
  12. package/dist/auth/index.d.ts.map +1 -0
  13. package/dist/auth/index.js +14 -0
  14. package/dist/auth/jwt.d.ts +24 -0
  15. package/dist/auth/jwt.d.ts.map +1 -0
  16. package/dist/auth/jwt.js +43 -0
  17. package/dist/auth/middleware.d.ts +22 -0
  18. package/dist/auth/middleware.d.ts.map +1 -0
  19. package/dist/auth/middleware.js +36 -0
  20. package/dist/auth/ssh-keys.d.ts +21 -0
  21. package/dist/auth/ssh-keys.d.ts.map +1 -0
  22. package/dist/auth/ssh-keys.js +59 -0
  23. package/dist/auth/types.d.ts +165 -0
  24. package/dist/auth/types.d.ts.map +1 -0
  25. package/dist/auth/types.js +53 -0
  26. package/dist/backend.d.ts +248 -0
  27. package/dist/backend.d.ts.map +1 -0
  28. package/dist/backend.js +40 -0
  29. package/dist/backends/adapters.d.ts +99 -0
  30. package/dist/backends/adapters.d.ts.map +1 -0
  31. package/dist/backends/adapters.js +308 -0
  32. package/dist/backends/external-tracker.d.ts +133 -0
  33. package/dist/backends/external-tracker.d.ts.map +1 -0
  34. package/dist/backends/external-tracker.js +731 -0
  35. package/dist/backends/git-native.d.ts +69 -0
  36. package/dist/backends/git-native.d.ts.map +1 -0
  37. package/dist/backends/git-native.js +797 -0
  38. package/dist/backends/github-issues.d.ts +78 -0
  39. package/dist/backends/github-issues.d.ts.map +1 -0
  40. package/dist/backends/github-issues.js +806 -0
  41. package/dist/backends/index.d.ts +52 -0
  42. package/dist/backends/index.d.ts.map +1 -0
  43. package/dist/backends/index.js +151 -0
  44. package/dist/backends/server.d.ts +42 -0
  45. package/dist/backends/server.d.ts.map +1 -0
  46. package/dist/backends/server.js +305 -0
  47. package/dist/cli/auth-store.d.ts +49 -0
  48. package/dist/cli/auth-store.d.ts.map +1 -0
  49. package/dist/cli/auth-store.js +150 -0
  50. package/dist/cli/client-config.d.ts +10 -0
  51. package/dist/cli/client-config.d.ts.map +1 -0
  52. package/dist/cli/client-config.js +87 -0
  53. package/dist/cli/commands/ask.d.ts +3 -0
  54. package/dist/cli/commands/ask.d.ts.map +1 -0
  55. package/dist/cli/commands/ask.js +171 -0
  56. package/dist/cli/commands/auth.d.ts +3 -0
  57. package/dist/cli/commands/auth.d.ts.map +1 -0
  58. package/dist/cli/commands/auth.js +510 -0
  59. package/dist/cli/commands/breakpoints.d.ts +3 -0
  60. package/dist/cli/commands/breakpoints.d.ts.map +1 -0
  61. package/dist/cli/commands/breakpoints.js +311 -0
  62. package/dist/cli/commands/responder-loop.d.ts +3 -0
  63. package/dist/cli/commands/responder-loop.d.ts.map +1 -0
  64. package/dist/cli/commands/responder-loop.js +78 -0
  65. package/dist/cli/commands/responders.d.ts +3 -0
  66. package/dist/cli/commands/responders.d.ts.map +1 -0
  67. package/dist/cli/commands/responders.js +157 -0
  68. package/dist/cli/commands/rules.d.ts +3 -0
  69. package/dist/cli/commands/rules.d.ts.map +1 -0
  70. package/dist/cli/commands/rules.js +105 -0
  71. package/dist/cli/commands/server.d.ts +3 -0
  72. package/dist/cli/commands/server.d.ts.map +1 -0
  73. package/dist/cli/commands/server.js +34 -0
  74. package/dist/cli/commands/tasks.d.ts +3 -0
  75. package/dist/cli/commands/tasks.d.ts.map +1 -0
  76. package/dist/cli/commands/tasks.js +281 -0
  77. package/dist/cli/commands/templates.d.ts +3 -0
  78. package/dist/cli/commands/templates.d.ts.map +1 -0
  79. package/dist/cli/commands/templates.js +100 -0
  80. package/dist/cli/index.d.ts +4 -0
  81. package/dist/cli/index.d.ts.map +1 -0
  82. package/dist/cli/index.js +9 -0
  83. package/dist/cli/output.d.ts +26 -0
  84. package/dist/cli/output.d.ts.map +1 -0
  85. package/dist/cli/output.js +143 -0
  86. package/dist/cli/program.d.ts +6 -0
  87. package/dist/cli/program.d.ts.map +1 -0
  88. package/dist/cli/program.js +38 -0
  89. package/dist/cli/tasks-adapter.d.ts +3 -0
  90. package/dist/cli/tasks-adapter.d.ts.map +1 -0
  91. package/dist/cli/tasks-adapter.js +4 -0
  92. package/dist/client/answer-poller.d.ts +52 -0
  93. package/dist/client/answer-poller.d.ts.map +1 -0
  94. package/dist/client/answer-poller.js +200 -0
  95. package/dist/client/auth-client.d.ts +200 -0
  96. package/dist/client/auth-client.d.ts.map +1 -0
  97. package/dist/client/auth-client.js +309 -0
  98. package/dist/client/breakpoint-router.d.ts +45 -0
  99. package/dist/client/breakpoint-router.d.ts.map +1 -0
  100. package/dist/client/breakpoint-router.js +45 -0
  101. package/dist/client/index.d.ts +17 -0
  102. package/dist/client/index.d.ts.map +1 -0
  103. package/dist/client/index.js +16 -0
  104. package/dist/client/profile-validator.d.ts +34 -0
  105. package/dist/client/profile-validator.d.ts.map +1 -0
  106. package/dist/client/profile-validator.js +89 -0
  107. package/dist/client/responder-client.d.ts +39 -0
  108. package/dist/client/responder-client.d.ts.map +1 -0
  109. package/dist/client/responder-client.js +72 -0
  110. package/dist/client/responder-matcher.d.ts +49 -0
  111. package/dist/client/responder-matcher.d.ts.map +1 -0
  112. package/dist/client/responder-matcher.js +226 -0
  113. package/dist/client/server-client.d.ts +124 -0
  114. package/dist/client/server-client.d.ts.map +1 -0
  115. package/dist/client/server-client.js +266 -0
  116. package/dist/client/timeout-manager.d.ts +47 -0
  117. package/dist/client/timeout-manager.d.ts.map +1 -0
  118. package/dist/client/timeout-manager.js +77 -0
  119. package/dist/config.d.ts +20 -0
  120. package/dist/config.d.ts.map +1 -0
  121. package/dist/config.js +93 -0
  122. package/dist/harness/index.d.ts +4 -0
  123. package/dist/harness/index.d.ts.map +1 -0
  124. package/dist/harness/index.js +2 -0
  125. package/dist/harness/interaction-provider.d.ts +71 -0
  126. package/dist/harness/interaction-provider.d.ts.map +1 -0
  127. package/dist/harness/interaction-provider.js +124 -0
  128. package/dist/harness/routing-rules.d.ts +7 -0
  129. package/dist/harness/routing-rules.d.ts.map +1 -0
  130. package/dist/harness/routing-rules.js +37 -0
  131. package/dist/index.d.ts +29 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +33 -0
  134. package/dist/mcp/backend-resolver.d.ts +43 -0
  135. package/dist/mcp/backend-resolver.d.ts.map +1 -0
  136. package/dist/mcp/backend-resolver.js +111 -0
  137. package/dist/mcp/http-transport.d.ts +37 -0
  138. package/dist/mcp/http-transport.d.ts.map +1 -0
  139. package/dist/mcp/http-transport.js +103 -0
  140. package/dist/mcp/index.d.ts +16 -0
  141. package/dist/mcp/index.d.ts.map +1 -0
  142. package/dist/mcp/index.js +12 -0
  143. package/dist/mcp/server.d.ts +20 -0
  144. package/dist/mcp/server.d.ts.map +1 -0
  145. package/dist/mcp/server.js +259 -0
  146. package/dist/mcp/tools/answer-breakpoint.d.ts +32 -0
  147. package/dist/mcp/tools/answer-breakpoint.d.ts.map +1 -0
  148. package/dist/mcp/tools/answer-breakpoint.js +45 -0
  149. package/dist/mcp/tools/ask-breakpoint.d.ts +58 -0
  150. package/dist/mcp/tools/ask-breakpoint.d.ts.map +1 -0
  151. package/dist/mcp/tools/ask-breakpoint.js +78 -0
  152. package/dist/mcp/tools/check-status.d.ts +16 -0
  153. package/dist/mcp/tools/check-status.d.ts.map +1 -0
  154. package/dist/mcp/tools/check-status.js +18 -0
  155. package/dist/mcp/tools/claim-breakpoint.d.ts +18 -0
  156. package/dist/mcp/tools/claim-breakpoint.d.ts.map +1 -0
  157. package/dist/mcp/tools/claim-breakpoint.js +28 -0
  158. package/dist/mcp/tools/list-breakpoints.d.ts +16 -0
  159. package/dist/mcp/tools/list-breakpoints.d.ts.map +1 -0
  160. package/dist/mcp/tools/list-breakpoints.js +14 -0
  161. package/dist/mcp/tools/list-responders.d.ts +18 -0
  162. package/dist/mcp/tools/list-responders.d.ts.map +1 -0
  163. package/dist/mcp/tools/list-responders.js +37 -0
  164. package/dist/mcp/tools/native-tasks.d.ts +270 -0
  165. package/dist/mcp/tools/native-tasks.d.ts.map +1 -0
  166. package/dist/mcp/tools/native-tasks.js +481 -0
  167. package/dist/mcp/tools/poll-breakpoints.d.ts +18 -0
  168. package/dist/mcp/tools/poll-breakpoints.d.ts.map +1 -0
  169. package/dist/mcp/tools/poll-breakpoints.js +36 -0
  170. package/dist/mcp/tools/verify-answer.d.ts +16 -0
  171. package/dist/mcp/tools/verify-answer.d.ts.map +1 -0
  172. package/dist/mcp/tools/verify-answer.js +38 -0
  173. package/dist/proven/index.d.ts +5 -0
  174. package/dist/proven/index.d.ts.map +1 -0
  175. package/dist/proven/index.js +3 -0
  176. package/dist/proven/keys.d.ts +33 -0
  177. package/dist/proven/keys.d.ts.map +1 -0
  178. package/dist/proven/keys.js +117 -0
  179. package/dist/proven/sign.d.ts +16 -0
  180. package/dist/proven/sign.d.ts.map +1 -0
  181. package/dist/proven/sign.js +60 -0
  182. package/dist/proven/types.d.ts +26 -0
  183. package/dist/proven/types.d.ts.map +1 -0
  184. package/dist/proven/types.js +5 -0
  185. package/dist/proven/verify.d.ts +6 -0
  186. package/dist/proven/verify.d.ts.map +1 -0
  187. package/dist/proven/verify.js +58 -0
  188. package/dist/responders/types.d.ts +38 -0
  189. package/dist/responders/types.d.ts.map +1 -0
  190. package/dist/responders/types.js +1 -0
  191. package/dist/router.d.ts +51 -0
  192. package/dist/router.d.ts.map +1 -0
  193. package/dist/router.js +200 -0
  194. package/dist/types.d.ts +7711 -0
  195. package/dist/types.d.ts.map +1 -0
  196. package/dist/types.js +479 -0
  197. package/package.json +96 -0
  198. package/responder/README.md +42 -0
  199. package/responder/backend-responder.json +9 -0
  200. package/responder/devops-responder.json +9 -0
  201. package/responder/frontend-responder.json +9 -0
  202. package/responder/schema.json +89 -0
@@ -0,0 +1,117 @@
1
+ import { generateKeyPairSync, createHash } from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import * as path from "node:path";
4
+ /**
5
+ * Generate a new Ed25519 key pair for a responder.
6
+ */
7
+ export function generateKeyPair(responderId, responderName) {
8
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
9
+ const pubDer = publicKey.export({ type: "spki", format: "der" });
10
+ const privPkcs8 = privateKey.export({ type: "pkcs8", format: "der" });
11
+ const fingerprint = createHash("sha256").update(pubDer).digest("hex");
12
+ const now = new Date().toISOString();
13
+ return {
14
+ publicKeyRecord: {
15
+ publicKey: pubDer.toString("base64"),
16
+ metadata: {
17
+ fingerprint,
18
+ responderId,
19
+ responderName,
20
+ createdAt: now,
21
+ },
22
+ },
23
+ privateKeyRecord: {
24
+ privateKey: privPkcs8.toString("base64"),
25
+ fingerprint,
26
+ responderId,
27
+ },
28
+ };
29
+ }
30
+ /**
31
+ * Save a public key to the trusted keys directory (git-tracked).
32
+ */
33
+ export async function saveTrustedPublicKey(publicKeyRecord, baseDir) {
34
+ const dir = baseDir
35
+ ? path.join(baseDir, ".keys", "trusted")
36
+ : path.resolve(process.cwd(), ".breakpoints", ".keys", "trusted");
37
+ await fs.mkdir(dir, { recursive: true });
38
+ const filePath = path.join(dir, `${publicKeyRecord.metadata.fingerprint}.pub.json`);
39
+ await fs.writeFile(filePath, JSON.stringify(publicKeyRecord, null, 2) + "\n", "utf-8");
40
+ return filePath;
41
+ }
42
+ /**
43
+ * Save a private key to the private keys directory (gitignored).
44
+ */
45
+ export async function savePrivateKey(privateKeyRecord, baseDir) {
46
+ const dir = baseDir
47
+ ? path.join(baseDir, ".keys", "private")
48
+ : path.resolve(process.cwd(), ".breakpoints", ".keys", "private");
49
+ await fs.mkdir(dir, { recursive: true });
50
+ const filePath = path.join(dir, `${privateKeyRecord.fingerprint}.key.json`);
51
+ await fs.writeFile(filePath, JSON.stringify(privateKeyRecord, null, 2) + "\n", "utf-8");
52
+ return filePath;
53
+ }
54
+ /**
55
+ * Load all trusted public keys from the trusted directory.
56
+ */
57
+ export async function loadTrustedPublicKeys(baseDir) {
58
+ const dir = baseDir
59
+ ? path.join(baseDir, ".keys", "trusted")
60
+ : path.resolve(process.cwd(), ".breakpoints", ".keys", "trusted");
61
+ let files;
62
+ try {
63
+ files = await fs.readdir(dir);
64
+ }
65
+ catch {
66
+ return [];
67
+ }
68
+ const keys = [];
69
+ for (const file of files) {
70
+ if (!file.endsWith(".pub.json"))
71
+ continue;
72
+ const raw = await fs.readFile(path.join(dir, file), "utf-8");
73
+ keys.push(JSON.parse(raw));
74
+ }
75
+ return keys;
76
+ }
77
+ /**
78
+ * Load a private key by fingerprint.
79
+ */
80
+ export async function loadPrivateKey(fingerprint, baseDir) {
81
+ const dir = baseDir
82
+ ? path.join(baseDir, ".keys", "private")
83
+ : path.resolve(process.cwd(), ".breakpoints", ".keys", "private");
84
+ const filePath = path.join(dir, `${fingerprint}.key.json`);
85
+ try {
86
+ const raw = await fs.readFile(filePath, "utf-8");
87
+ return JSON.parse(raw);
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ /**
94
+ * Rotate a key: generate new pair, save both, mark old public key as expired.
95
+ */
96
+ export async function rotateKey(responderId, responderName, oldFingerprint, baseDir) {
97
+ // Mark old key as expired
98
+ const trustedDir = baseDir
99
+ ? path.join(baseDir, ".keys", "trusted")
100
+ : path.resolve(process.cwd(), ".breakpoints", ".keys", "trusted");
101
+ const oldKeyPath = path.join(trustedDir, `${oldFingerprint}.pub.json`);
102
+ try {
103
+ const raw = await fs.readFile(oldKeyPath, "utf-8");
104
+ const oldKey = JSON.parse(raw);
105
+ oldKey.metadata.expiresAt = new Date().toISOString();
106
+ oldKey.metadata.note = `Rotated. Superseded by new key for ${responderId}.`;
107
+ await fs.writeFile(oldKeyPath, JSON.stringify(oldKey, null, 2) + "\n", "utf-8");
108
+ }
109
+ catch {
110
+ // Old key may not exist; that's fine
111
+ }
112
+ // Generate and save new pair
113
+ const newPair = generateKeyPair(responderId, responderName);
114
+ await saveTrustedPublicKey(newPair.publicKeyRecord, baseDir);
115
+ await savePrivateKey(newPair.privateKeyRecord, baseDir);
116
+ return newPair;
117
+ }
@@ -0,0 +1,16 @@
1
+ import type { BreakpointAnswer, ProvenBreakpointAnswer } from "../types.js";
2
+ import type { PrivateKeyRecord } from "./types.js";
3
+ /**
4
+ * Sign a BreakpointAnswer with the responder's private key.
5
+ *
6
+ * Returns a ProvenBreakpointAnswer with the signature and key metadata.
7
+ */
8
+ export declare function signAnswer(answer: BreakpointAnswer, fingerprint: string, baseDir?: string): Promise<ProvenBreakpointAnswer>;
9
+ /**
10
+ * Sign a BreakpointAnswer using an already-loaded PrivateKeyRecord.
11
+ *
12
+ * This avoids requiring the key to be persisted on disk and is useful
13
+ * for backends that load the key from a configured path.
14
+ */
15
+ export declare function signAnswerWithKeyRecord(answer: BreakpointAnswer, keyRecord: PrivateKeyRecord): ProvenBreakpointAnswer;
16
+ //# sourceMappingURL=sign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../../src/proven/sign.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA4BnD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,sBAAsB,CAAC,CAOjC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,gBAAgB,GAC1B,sBAAsB,CAiBxB"}
@@ -0,0 +1,60 @@
1
+ import { createPrivateKey, sign as cryptoSign } from "node:crypto";
2
+ import { loadPrivateKey } from "./keys.js";
3
+ /**
4
+ * Fields included in the signature. The order is canonical.
5
+ */
6
+ const SIGNED_FIELDS = [
7
+ "id",
8
+ "breakpointId",
9
+ "responderId",
10
+ "text",
11
+ "approved",
12
+ "confidence",
13
+ "answeredAt",
14
+ ];
15
+ /**
16
+ * Build the canonical signing payload from an answer.
17
+ * Fields are sorted, null/undefined serialized as empty string.
18
+ */
19
+ function buildSigningPayload(answer) {
20
+ const parts = [];
21
+ for (const field of SIGNED_FIELDS) {
22
+ const value = answer[field];
23
+ parts.push(`${field}=${value ?? ""}`);
24
+ }
25
+ return Buffer.from(parts.join("\n"), "utf-8");
26
+ }
27
+ /**
28
+ * Sign a BreakpointAnswer with the responder's private key.
29
+ *
30
+ * Returns a ProvenBreakpointAnswer with the signature and key metadata.
31
+ */
32
+ export async function signAnswer(answer, fingerprint, baseDir) {
33
+ const keyRecord = await loadPrivateKey(fingerprint, baseDir);
34
+ if (!keyRecord) {
35
+ throw new Error(`Private key not found for fingerprint: ${fingerprint}`);
36
+ }
37
+ return signAnswerWithKeyRecord(answer, keyRecord);
38
+ }
39
+ /**
40
+ * Sign a BreakpointAnswer using an already-loaded PrivateKeyRecord.
41
+ *
42
+ * This avoids requiring the key to be persisted on disk and is useful
43
+ * for backends that load the key from a configured path.
44
+ */
45
+ export function signAnswerWithKeyRecord(answer, keyRecord) {
46
+ const privateKey = createPrivateKey({
47
+ key: Buffer.from(keyRecord.privateKey, "base64"),
48
+ format: "der",
49
+ type: "pkcs8",
50
+ });
51
+ const payload = buildSigningPayload(answer);
52
+ const signature = cryptoSign(null, payload, privateKey);
53
+ return {
54
+ ...answer,
55
+ signature: signature.toString("base64"),
56
+ publicKeyFingerprint: keyRecord.fingerprint,
57
+ signedAt: new Date().toISOString(),
58
+ signedFields: [...SIGNED_FIELDS],
59
+ };
60
+ }
@@ -0,0 +1,26 @@
1
+ export interface KeyPairMetadata {
2
+ /** Hex-encoded fingerprint (SHA-256 of public key DER). */
3
+ fingerprint: string;
4
+ /** Responder identity associated with this key. */
5
+ responderId: string;
6
+ responderName: string;
7
+ /** ISO timestamp of key creation. */
8
+ createdAt: string;
9
+ /** ISO timestamp when this key should no longer be used for signing. */
10
+ expiresAt?: string;
11
+ /** Human-readable note. */
12
+ note?: string;
13
+ }
14
+ export interface PublicKeyRecord {
15
+ /** Base64-encoded Ed25519 public key (DER/SPKI format). */
16
+ publicKey: string;
17
+ metadata: KeyPairMetadata;
18
+ }
19
+ export interface PrivateKeyRecord {
20
+ /** Base64-encoded Ed25519 private key (PKCS8 format). */
21
+ privateKey: string;
22
+ /** Fingerprint linking to the corresponding public key. */
23
+ fingerprint: string;
24
+ responderId: string;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/proven/types.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB"}
@@ -0,0 +1,5 @@
1
+ // ── Proven-specific types ───────────────────────────────────────────────
2
+ // These types are used internally by the proven subsystem for key management.
3
+ // The ProvenBreakpointAnswer and ProvenVerificationResult schemas live in
4
+ // src/types.ts as they are part of the public domain model.
5
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { ProvenBreakpointAnswer, ProvenVerificationResult } from "../types.js";
2
+ /**
3
+ * Verify a proven breakpoint answer against trusted public keys.
4
+ */
5
+ export declare function verifyAnswer(provenAnswer: ProvenBreakpointAnswer, baseDir?: string): Promise<ProvenVerificationResult>;
6
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/proven/verify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,wBAAwB,EAAoB,MAAM,aAAa,CAAC;AAetG;;GAEG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,sBAAsB,EACpC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,CAAC,CAiDnC"}
@@ -0,0 +1,58 @@
1
+ import { createPublicKey, verify as cryptoVerify } from "node:crypto";
2
+ import { loadTrustedPublicKeys } from "./keys.js";
3
+ /**
4
+ * Rebuild the canonical signing payload for verification.
5
+ */
6
+ function buildSigningPayload(answer, signedFields) {
7
+ const parts = [];
8
+ for (const field of signedFields) {
9
+ const value = answer[field];
10
+ parts.push(`${field}=${value ?? ""}`);
11
+ }
12
+ return Buffer.from(parts.join("\n"), "utf-8");
13
+ }
14
+ /**
15
+ * Verify a proven breakpoint answer against trusted public keys.
16
+ */
17
+ export async function verifyAnswer(provenAnswer, baseDir) {
18
+ const trustedKeys = await loadTrustedPublicKeys(baseDir);
19
+ const matchingKey = trustedKeys.find((k) => k.metadata.fingerprint === provenAnswer.publicKeyFingerprint);
20
+ if (!matchingKey) {
21
+ return {
22
+ valid: false,
23
+ publicKeyFingerprint: provenAnswer.publicKeyFingerprint,
24
+ reason: "Public key not found in trusted keys",
25
+ verifiedAt: new Date().toISOString(),
26
+ };
27
+ }
28
+ // Check key expiration
29
+ if (matchingKey.metadata.expiresAt) {
30
+ const expiresAt = new Date(matchingKey.metadata.expiresAt);
31
+ const signedAt = new Date(provenAnswer.signedAt);
32
+ if (signedAt > expiresAt) {
33
+ return {
34
+ valid: false,
35
+ publicKeyFingerprint: provenAnswer.publicKeyFingerprint,
36
+ responderName: matchingKey.metadata.responderName,
37
+ reason: "Key was expired at time of signing",
38
+ verifiedAt: new Date().toISOString(),
39
+ };
40
+ }
41
+ }
42
+ // Verify signature
43
+ const publicKey = createPublicKey({
44
+ key: Buffer.from(matchingKey.publicKey, "base64"),
45
+ format: "der",
46
+ type: "spki",
47
+ });
48
+ const payload = buildSigningPayload(provenAnswer, provenAnswer.signedFields);
49
+ const signatureBuffer = Buffer.from(provenAnswer.signature, "base64");
50
+ const isValid = cryptoVerify(null, payload, publicKey, signatureBuffer);
51
+ return {
52
+ valid: isValid,
53
+ publicKeyFingerprint: provenAnswer.publicKeyFingerprint,
54
+ responderName: matchingKey.metadata.responderName,
55
+ reason: isValid ? "Signature verified successfully" : "Signature verification failed",
56
+ verifiedAt: new Date().toISOString(),
57
+ };
58
+ }
@@ -0,0 +1,38 @@
1
+ import type { ResponderProfile, ResponderType } from "../types.js";
2
+ export type { ResponderProfile, ResponderType } from "../types.js";
3
+ export interface TaskRoutingHints {
4
+ external?: boolean;
5
+ responderType?: ResponderType;
6
+ preferredResponderId?: string;
7
+ capabilities?: string[];
8
+ requiredCapabilities?: string[];
9
+ adapter?: string;
10
+ model?: string;
11
+ provider?: string;
12
+ trackerBackend?: string;
13
+ fallbackType?: ResponderType;
14
+ }
15
+ export interface BaseResponder extends ResponderProfile {
16
+ type: Exclude<ResponderType, "auto">;
17
+ capabilities: string[];
18
+ }
19
+ export interface HumanResponder extends BaseResponder {
20
+ type: "human";
21
+ }
22
+ export interface AgentResponder extends BaseResponder {
23
+ type: "agent";
24
+ adapter?: string;
25
+ model?: string;
26
+ provider?: string;
27
+ }
28
+ export interface TrackerResponder extends BaseResponder {
29
+ type: "tracker";
30
+ trackerBackend?: string;
31
+ trackerConfig?: Record<string, unknown>;
32
+ }
33
+ export interface InternalResponder extends BaseResponder {
34
+ type: "internal";
35
+ }
36
+ export type Responder = HumanResponder | AgentResponder | TrackerResponder | InternalResponder;
37
+ export type RoutedResponder = Responder;
38
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/responders/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,IAAI,EAAE,SAAS,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,SAAS,GACjB,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,iBAAiB,CAAC;AAEtB,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ import type { BreakpointBackend } from "./backend.js";
2
+ import type { ResponderProfile, ResponderType } from "./types.js";
3
+ import type { RoutedResponder, TaskRoutingHints } from "./responders/types.js";
4
+ export interface RoutableTaskDef {
5
+ kind: string;
6
+ title?: string;
7
+ agent?: TaskRoutingHints & Record<string, unknown>;
8
+ breakpoint?: TaskRoutingHints & Record<string, unknown>;
9
+ metadata?: TaskRoutingHints & Record<string, unknown>;
10
+ }
11
+ export interface TaskRouteContext {
12
+ agentBackend?: BreakpointBackend;
13
+ humanBackend?: BreakpointBackend;
14
+ trackerBackend?: BreakpointBackend;
15
+ responders?: ResponderProfile[];
16
+ }
17
+ export type TaskRouteDecision = {
18
+ responderType: "internal";
19
+ responder: RoutedResponder;
20
+ route: "agent-core";
21
+ reason: string;
22
+ } | {
23
+ responderType: "human";
24
+ responder: RoutedResponder;
25
+ route: "breakpoint";
26
+ backend?: BreakpointBackend;
27
+ reason: string;
28
+ } | {
29
+ responderType: "agent";
30
+ responder: RoutedResponder;
31
+ route: "adapters";
32
+ backend?: BreakpointBackend;
33
+ reason: string;
34
+ } | {
35
+ responderType: "tracker";
36
+ responder?: RoutedResponder;
37
+ route: "external-tracker";
38
+ backend?: BreakpointBackend;
39
+ unavailable?: boolean;
40
+ reason: string;
41
+ };
42
+ export declare function routeTask(task: RoutableTaskDef, context?: TaskRouteContext): TaskRouteDecision;
43
+ export declare class TaskRouter {
44
+ private readonly context;
45
+ constructor(context?: TaskRouteContext);
46
+ routeTask(task: RoutableTaskDef, context?: TaskRouteContext): TaskRouteDecision;
47
+ matchResponder(type: Exclude<ResponderType, "auto">, hints?: TaskRoutingHints): RoutedResponder | undefined;
48
+ }
49
+ export declare function routingHints(task: RoutableTaskDef): TaskRoutingHints;
50
+ export declare function isHostDelegableRoute(decision: TaskRouteDecision): boolean;
51
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxD,QAAQ,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,cAAc,CAAC,EAAE,iBAAiB,CAAC;IACnC,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAED,MAAM,MAAM,iBAAiB,GACzB;IACE,aAAa,EAAE,UAAU,CAAC;IAC1B,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,aAAa,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,wBAAgB,SAAS,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,GAAE,gBAAqB,GAAG,iBAAiB,CAElG;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;gBAE/B,OAAO,GAAE,gBAAqB;IAI1C,SAAS,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,GAAE,gBAAqB,GAAG,iBAAiB;IA0CnF,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,EAAE,KAAK,GAAE,gBAAqB,GAAG,eAAe,GAAG,SAAS;CAGhH;AA6BD,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,gBAAgB,CAcpE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAEzE"}
package/dist/router.js ADDED
@@ -0,0 +1,200 @@
1
+ export function routeTask(task, context = {}) {
2
+ return new TaskRouter(context).routeTask(task);
3
+ }
4
+ export class TaskRouter {
5
+ context;
6
+ constructor(context = {}) {
7
+ this.context = context;
8
+ }
9
+ routeTask(task, context = {}) {
10
+ const mergedContext = { ...this.context, ...context };
11
+ const hints = routingHints(task);
12
+ const requested = hints.responderType ?? (hints.external ? "agent" : defaultResponderType(task));
13
+ if (requested === "auto") {
14
+ const agent = selectResponder(mergedContext.responders, "agent", preferredResponder(hints), requiredCapabilities(hints));
15
+ if (agent || (mergedContext.agentBackend && !hasResponderInventory(mergedContext))) {
16
+ return agentDecision(hints, mergedContext, agent, "auto selected available agent responder");
17
+ }
18
+ return humanDecision(mergedContext, "auto fell back to human responder", hints);
19
+ }
20
+ if (requested === "agent") {
21
+ const responder = selectResponder(mergedContext.responders, "agent", preferredResponder(hints), requiredCapabilities(hints));
22
+ if (!responder && !mergedContext.agentBackend && hints.fallbackType && hints.fallbackType !== "agent") {
23
+ return fallbackDecision(hints.fallbackType, task, mergedContext, `agent responder unavailable; fell back to ${hints.fallbackType}`);
24
+ }
25
+ return agentDecision(hints, mergedContext, responder, "agent responder requested");
26
+ }
27
+ if (requested === "human") {
28
+ return humanDecision(mergedContext, "human responder requested", hints);
29
+ }
30
+ if (requested === "tracker") {
31
+ const responder = selectResponder(mergedContext.responders, "tracker", hints.trackerBackend, requiredCapabilities(hints));
32
+ return {
33
+ responderType: "tracker",
34
+ responder,
35
+ route: "external-tracker",
36
+ backend: mergedContext.trackerBackend,
37
+ unavailable: !mergedContext.trackerBackend,
38
+ reason: mergedContext.trackerBackend
39
+ ? "tracker responder requested"
40
+ : `ExternalTrackerBackend unavailable for ${hints.trackerBackend ?? "default tracker"}`,
41
+ };
42
+ }
43
+ return internalDecision("internal responder requested");
44
+ }
45
+ matchResponder(type, hints = {}) {
46
+ return selectResponder(this.context.responders, type, preferredResponder(hints), requiredCapabilities(hints));
47
+ }
48
+ }
49
+ function fallbackDecision(fallbackType, task, context, reason) {
50
+ if (fallbackType === "human") {
51
+ return humanDecision(context, reason, routingHints(task));
52
+ }
53
+ if (fallbackType === "tracker") {
54
+ const hints = routingHints(task);
55
+ const responder = selectResponder(context.responders, "tracker", hints.trackerBackend, requiredCapabilities(hints));
56
+ return {
57
+ responderType: "tracker",
58
+ responder,
59
+ route: "external-tracker",
60
+ backend: context.trackerBackend,
61
+ unavailable: !context.trackerBackend,
62
+ reason,
63
+ };
64
+ }
65
+ if (fallbackType === "auto") {
66
+ return humanDecision(context, reason);
67
+ }
68
+ return internalDecision(reason);
69
+ }
70
+ export function routingHints(task) {
71
+ const source = task.agent ?? task.breakpoint ?? {};
72
+ return {
73
+ responderType: source.responderType ?? task.metadata?.responderType,
74
+ external: source.external ?? task.metadata?.external,
75
+ preferredResponderId: source.preferredResponderId ?? task.metadata?.preferredResponderId,
76
+ capabilities: source.capabilities ?? task.metadata?.capabilities,
77
+ requiredCapabilities: source.requiredCapabilities ?? task.metadata?.requiredCapabilities,
78
+ adapter: source.adapter ?? task.metadata?.adapter,
79
+ model: source.model ?? task.metadata?.model,
80
+ provider: source.provider ?? task.metadata?.provider,
81
+ trackerBackend: source.trackerBackend ?? task.metadata?.trackerBackend,
82
+ fallbackType: source.fallbackType ?? task.metadata?.fallbackType,
83
+ };
84
+ }
85
+ export function isHostDelegableRoute(decision) {
86
+ return decision.route === "agent-core";
87
+ }
88
+ function defaultResponderType(task) {
89
+ if (task.kind === "breakpoint")
90
+ return "human";
91
+ return "internal";
92
+ }
93
+ function internalDecision(reason) {
94
+ return {
95
+ responderType: "internal",
96
+ route: "agent-core",
97
+ reason,
98
+ responder: {
99
+ id: "agent-core",
100
+ type: "internal",
101
+ name: "Internal Agent",
102
+ title: "Internal Agent",
103
+ capabilities: ["text", "agent-core", "internal"],
104
+ domains: [],
105
+ tags: ["internal"],
106
+ availability: true,
107
+ responseTimeSla: 1,
108
+ },
109
+ };
110
+ }
111
+ function humanDecision(context, reason, hints = {}) {
112
+ const responder = selectResponder(context.responders, "human", preferredResponder(hints), requiredCapabilities(hints)) ?? {
113
+ id: "human",
114
+ type: "human",
115
+ name: "Human Responder",
116
+ title: "Human Responder",
117
+ capabilities: ["text", "review", "approval"],
118
+ domains: [],
119
+ tags: ["human"],
120
+ availability: true,
121
+ responseTimeSla: 300_000,
122
+ };
123
+ return { responderType: "human", route: "breakpoint", backend: context.humanBackend, responder, reason };
124
+ }
125
+ function agentDecision(hints, context, responder, reason) {
126
+ return {
127
+ responderType: "agent",
128
+ route: "adapters",
129
+ backend: context.agentBackend,
130
+ reason,
131
+ responder: responder ?? {
132
+ id: hints.adapter ?? "adapters",
133
+ type: "agent",
134
+ name: hints.adapter ?? "AgentMux Responder",
135
+ title: "AgentMux Responder",
136
+ capabilities: requiredCapabilities(hints),
137
+ domains: [],
138
+ tags: ["agent"],
139
+ availability: true,
140
+ responseTimeSla: 300_000,
141
+ adapter: hints.adapter,
142
+ model: hints.model,
143
+ provider: hints.provider,
144
+ },
145
+ };
146
+ }
147
+ function selectResponder(responders, type, preferred, capabilities = []) {
148
+ const available = responders
149
+ ?.map((responder) => normalizeResponder(responder))
150
+ .filter((responder) => responder.type === type &&
151
+ responder.availability &&
152
+ hasCapabilities(responder, capabilities)) ?? [];
153
+ if (preferred) {
154
+ const preferredMatch = available.find((responder) => responder.id === preferred ||
155
+ responder.adapter === preferred ||
156
+ responder.trackerBackend === preferred);
157
+ if (preferredMatch)
158
+ return preferredMatch;
159
+ }
160
+ return available.sort(compareResponderPriority)[0];
161
+ }
162
+ function normalizeResponder(responder) {
163
+ const type = responder.type ?? "human";
164
+ return {
165
+ ...responder,
166
+ type,
167
+ capabilities: normalizedCapabilities(responder),
168
+ };
169
+ }
170
+ function normalizedCapabilities(responder) {
171
+ return uniqueLowercase([
172
+ ...(responder.capabilities ?? []),
173
+ ...responder.domains,
174
+ ...responder.tags,
175
+ ]);
176
+ }
177
+ function hasCapabilities(responder, capabilities) {
178
+ if (capabilities.length === 0)
179
+ return true;
180
+ const available = new Set(responder.capabilities.map((capability) => capability.toLowerCase()));
181
+ return capabilities.every((capability) => available.has(capability.toLowerCase()));
182
+ }
183
+ function requiredCapabilities(hints) {
184
+ return uniqueLowercase(hints.requiredCapabilities ?? hints.capabilities ?? []);
185
+ }
186
+ function preferredResponder(hints) {
187
+ return hints.preferredResponderId ?? hints.adapter ?? hints.trackerBackend;
188
+ }
189
+ function hasResponderInventory(context) {
190
+ return Array.isArray(context.responders);
191
+ }
192
+ function compareResponderPriority(a, b) {
193
+ const sla = a.responseTimeSla - b.responseTimeSla;
194
+ if (sla !== 0)
195
+ return sla;
196
+ return a.id.localeCompare(b.id);
197
+ }
198
+ function uniqueLowercase(values) {
199
+ return [...new Set(values.filter(Boolean).map((value) => value.toLowerCase()))];
200
+ }