@martinloop/mcp 0.8.9 → 0.9.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.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/dist/package-version.d.ts +1 -1
  3. package/dist/package-version.js +1 -1
  4. package/dist/server-validation.js +2 -3
  5. package/dist/server.d.ts +1 -0
  6. package/dist/server.js +181 -24
  7. package/dist/tools/get-status.d.ts +3 -0
  8. package/dist/tools/get-status.js +11 -2
  9. package/dist/tools/logs.d.ts +2 -0
  10. package/dist/tools/logs.js +25 -3
  11. package/dist/tools/run-controls.d.ts +51 -6
  12. package/dist/tools/run-controls.js +905 -35
  13. package/dist/tools/run-loop.js +7 -0
  14. package/dist/vendor/adapters/process-supervisor.d.ts +12 -0
  15. package/dist/vendor/adapters/process-supervisor.js +40 -2
  16. package/dist/vendor/adapters/verifier-only.js +4 -0
  17. package/dist/vendor/contracts/index.d.ts +8 -0
  18. package/dist/vendor/contracts/index.js +1 -0
  19. package/dist/vendor/contracts/swarm-timing.d.ts +7 -0
  20. package/dist/vendor/contracts/swarm-timing.js +22 -0
  21. package/dist/vendor/core/index.d.ts +37 -2
  22. package/dist/vendor/core/index.js +120 -14
  23. package/dist/vendor/core/persistence/index.d.ts +1 -1
  24. package/dist/vendor/core/persistence/index.js +1 -1
  25. package/dist/vendor/core/persistence/integrity.d.ts +1 -0
  26. package/dist/vendor/core/persistence/integrity.js +1 -1
  27. package/dist/vendor/core/policy.js +11 -1
  28. package/dist/vendor/core/resume.d.ts +25 -1
  29. package/dist/vendor/core/resume.js +187 -13
  30. package/dist/vendor/core/rollback.d.ts +14 -0
  31. package/dist/vendor/core/rollback.js +91 -34
  32. package/dist/vendor/core/swarm/live-runtime.js +2 -0
  33. package/dist/vendor/core/verified-handoff.js +62 -2
  34. package/package.json +3 -7
  35. package/server.json +2 -2
package/README.md CHANGED
@@ -81,6 +81,8 @@ Every attempt runs your verifier. Every dollar is tracked. If the agent drifts o
81
81
  **Run the loop:**
82
82
  `martin_doctor` `martin_plan` `martin_preflight` `martin_run` `martin_pause` `martin_continue` `martin_cancel`
83
83
 
84
+ The three run-control mutation tools are local-stdio only. They require a live MartinLoop executor lease, and their `local_os_user` authority label describes the same-machine boundary rather than an authenticated person. Remote HTTP sessions are rejected for these tools.
85
+
84
86
  **Inspect results:**
85
87
  `martin_status` `martin_logs` `martin_dossier` `martin_eval` `martin_inspect` `martin_list_runs` `martin_get_run` `martin_get_attempt` `martin_get_verification_results` `martin_triage_runs`
86
88
 
@@ -1 +1 @@
1
- export declare const MARTIN_MCP_PACKAGE_VERSION = "0.8.9";
1
+ export declare const MARTIN_MCP_PACKAGE_VERSION = "0.9.0";
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by build — do not edit by hand.
2
2
  // Source of truth: packages/mcp/package.json#version
3
- export const MARTIN_MCP_PACKAGE_VERSION = "0.8.9";
3
+ export const MARTIN_MCP_PACKAGE_VERSION = "0.9.0";
@@ -448,12 +448,11 @@ function validateEvalInput(args) {
448
448
  }
449
449
  function validateRunControlInput(args) {
450
450
  const record = requireObject(args);
451
- assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "reason", "requestedBy"]);
451
+ assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "reason"]);
452
452
  const base = validateGetRunInput(pickKeys(record, RUN_SELECTOR_KEYS));
453
453
  return {
454
454
  ...base,
455
- ...optionalString(record.reason, "reason"),
456
- ...optionalString(record.requestedBy, "requestedBy")
455
+ ...optionalString(record.reason, "reason")
457
456
  };
458
457
  }
459
458
  function validateCreatePrInput(args) {
package/dist/server.d.ts CHANGED
@@ -85,6 +85,7 @@ export declare function createMartinMcpServer(serverInfo?: {
85
85
  name?: string;
86
86
  version?: string;
87
87
  toolProfile?: MartinMcpToolProfile;
88
+ controlTransport?: "local_stdio" | "untrusted_remote";
88
89
  }): Server<{
89
90
  method: string;
90
91
  params?: {
package/dist/server.js CHANGED
@@ -377,6 +377,44 @@ const statusOutputSchema = {
377
377
  loop: loopPreviewSchema
378
378
  },
379
379
  required: ["loop"]
380
+ },
381
+ live: {
382
+ type: "object",
383
+ additionalProperties: false,
384
+ properties: {
385
+ phase: { type: "string" },
386
+ requestedControlState: {
387
+ type: "string",
388
+ enum: ["active", "paused", "cancellation_requested", "terminal"],
389
+ description: "Latest durable operator request; this does not prove executor application."
390
+ },
391
+ appliedControlState: {
392
+ type: "string",
393
+ enum: ["active", "paused", "cancellation_pending", "terminal"],
394
+ description: "Control state reconciled from validated executor application evidence."
395
+ },
396
+ pauseState: {
397
+ type: "string",
398
+ enum: ["active", "paused", "cancellation_requested"],
399
+ description: "Backwards-compatible applied control state; pending pause requests remain active."
400
+ },
401
+ approvalState: {
402
+ type: "string",
403
+ enum: ["not_required", "resume_requested"]
404
+ },
405
+ commandsRun: { type: "integer" },
406
+ filesTouched: { type: "integer" },
407
+ verifierStep: { type: "string" }
408
+ },
409
+ required: [
410
+ "phase",
411
+ "requestedControlState",
412
+ "appliedControlState",
413
+ "pauseState",
414
+ "approvalState",
415
+ "commandsRun",
416
+ "filesTouched"
417
+ ]
380
418
  }
381
419
  },
382
420
  required: [
@@ -392,7 +430,8 @@ const statusOutputSchema = {
392
430
  "remainingBudgetUsd",
393
431
  "remainingIterations",
394
432
  "budget",
395
- "inspection"
433
+ "inspection",
434
+ "live"
396
435
  ]
397
436
  };
398
437
  const doctorOutputSchema = {
@@ -746,10 +785,20 @@ const logsOutputSchema = {
746
785
  additionalProperties: false,
747
786
  properties: {
748
787
  lifecycleState: { type: "string", description: "Current run lifecycle state (e.g. running, completed, cancelled)." },
788
+ requestedControlState: {
789
+ type: "string",
790
+ enum: ["active", "paused", "cancellation_requested", "terminal"],
791
+ description: "Latest durable operator request; this does not prove executor application."
792
+ },
793
+ appliedControlState: {
794
+ type: "string",
795
+ enum: ["active", "paused", "cancellation_pending", "terminal"],
796
+ description: "Control state reconciled from validated executor application evidence."
797
+ },
749
798
  pauseState: {
750
799
  type: "string",
751
800
  enum: ["active", "paused", "cancellation_requested"],
752
- description: "Whether the run is active, paused, or has a pending cancellation."
801
+ description: "Backwards-compatible applied control state; pending pause requests remain active."
753
802
  },
754
803
  approvalState: {
755
804
  type: "string",
@@ -757,7 +806,7 @@ const logsOutputSchema = {
757
806
  description: "Whether the run is waiting at a human-approval checkpoint."
758
807
  }
759
808
  },
760
- required: ["lifecycleState", "pauseState", "approvalState"]
809
+ required: ["lifecycleState", "requestedControlState", "appliedControlState", "pauseState", "approvalState"]
761
810
  },
762
811
  entries: {
763
812
  type: "array",
@@ -781,6 +830,84 @@ const logsOutputSchema = {
781
830
  },
782
831
  required: ["source", "sourceKind", "loopId", "logCount", "live", "entries"]
783
832
  };
833
+ const controlReceiptSchema = {
834
+ type: "object",
835
+ additionalProperties: false,
836
+ properties: {
837
+ schemaVersion: { const: "martin.run-control.v2" },
838
+ controlId: { type: "string" },
839
+ loopId: { type: "string" },
840
+ action: { type: "string", enum: ["pause", "cancel", "continue"] },
841
+ requestedAt: { type: "string" },
842
+ authorityBoundary: { const: "local_os_user" },
843
+ principal: { const: "local_mcp_client" },
844
+ executorLeaseToken: { type: "string" },
845
+ workspaceId: { type: "string" },
846
+ projectId: { type: "string" },
847
+ expectedPreviousControlId: { type: "string" },
848
+ reason: { type: "string" },
849
+ previousReceiptHash: { type: "string", pattern: "^[a-f0-9]{64}$" },
850
+ receiptHash: { type: "string", pattern: "^[a-f0-9]{64}$" }
851
+ },
852
+ required: [
853
+ "schemaVersion",
854
+ "controlId",
855
+ "loopId",
856
+ "action",
857
+ "requestedAt",
858
+ "authorityBoundary",
859
+ "principal",
860
+ "executorLeaseToken",
861
+ "receiptHash"
862
+ ]
863
+ };
864
+ const controlApplicationSchema = {
865
+ type: "object",
866
+ additionalProperties: false,
867
+ properties: {
868
+ schemaVersion: { const: "martin.run-control-application.v2" },
869
+ loopId: { type: "string" },
870
+ controlId: { type: "string" },
871
+ action: { type: "string", enum: ["pause_applied", "continue_applied"] },
872
+ appliedAt: { type: "string" },
873
+ relatedControlId: { type: "string" },
874
+ receiptHash: { type: "string", pattern: "^[a-f0-9]{64}$" },
875
+ previousApplicationHash: { type: "string", pattern: "^[a-f0-9]{64}$" },
876
+ applicationHash: { type: "string", pattern: "^[a-f0-9]{64}$" }
877
+ },
878
+ required: ["schemaVersion", "loopId", "controlId", "action", "appliedAt", "receiptHash", "applicationHash"]
879
+ };
880
+ const controlPublicationSchema = {
881
+ type: "object",
882
+ additionalProperties: false,
883
+ properties: {
884
+ schemaVersion: { const: "martin.run-control-publication.v1" },
885
+ loopId: { type: "string" },
886
+ controlId: { type: "string" },
887
+ receiptHash: { type: "string", pattern: "^[a-f0-9]{64}$" },
888
+ signalState: { type: "string", enum: ["created", "already_exists"] },
889
+ recordedAt: { type: "string" },
890
+ previousPublicationHash: { type: "string", pattern: "^[a-f0-9]{64}$" },
891
+ publicationHash: { type: "string", pattern: "^[a-f0-9]{64}$" }
892
+ },
893
+ required: ["schemaVersion", "loopId", "controlId", "receiptHash", "signalState", "recordedAt", "publicationHash"]
894
+ };
895
+ const controlStateSchema = {
896
+ type: "object",
897
+ additionalProperties: false,
898
+ properties: {
899
+ requestedState: { type: "string", enum: ["active", "paused", "cancellation_requested", "terminal"] },
900
+ latestReceipt: controlReceiptSchema,
901
+ approvalState: { type: "string", enum: ["not_required", "resume_requested"] },
902
+ appliedState: { type: "string", enum: ["active", "paused", "cancellation_pending", "terminal"] },
903
+ receipts: { type: "array", items: controlReceiptSchema },
904
+ applications: { type: "array", items: controlApplicationSchema },
905
+ publications: { type: "array", items: controlPublicationSchema },
906
+ receiptPath: { type: "string" },
907
+ applicationPath: { type: "string" }
908
+ },
909
+ required: ["requestedState", "approvalState", "appliedState", "receipts", "applications", "publications"]
910
+ };
784
911
  const controlOutputSchema = {
785
912
  type: "object",
786
913
  additionalProperties: false,
@@ -793,7 +920,6 @@ const controlOutputSchema = {
793
920
  },
794
921
  controlId: { type: "string", description: "Unique receipt ID for this control action." },
795
922
  requestedAt: { type: "string", description: "ISO 8601 timestamp when the control was recorded." },
796
- requestedBy: { type: "string", description: "Identity that requested the control, if provided." },
797
923
  reason: { type: "string", description: "Human-readable reason for the control, if provided." },
798
924
  ok: { type: "boolean", description: "Whether the control receipt was recorded." },
799
925
  summary: { type: "string", description: "Human-readable result of the control request." },
@@ -802,13 +928,36 @@ const controlOutputSchema = {
802
928
  enum: ["pause", "cancel", "continue"],
803
929
  description: "The control action that was requested."
804
930
  },
805
- state: {
806
- type: "object",
807
- additionalProperties: true,
808
- description: "Run control state after this request, including all recorded receipts."
809
- }
931
+ publicationState: {
932
+ type: "string",
933
+ enum: ["published", "already_requested", "receipt_recorded", "published_audit_pending"],
934
+ description: "Whether an authoritative cancellation signal was published or a cooperative receipt was recorded."
935
+ },
936
+ applicationState: {
937
+ type: "string",
938
+ enum: ["pending_observation", "requested_only", "terminal_reconciled"],
939
+ description: "Whether the active executor has applied the control; publication alone is not application proof."
940
+ },
941
+ authorityBoundary: { const: "local_os_user" },
942
+ principal: { const: "local_mcp_client" },
943
+ receiptHash: { type: "string", pattern: "^[a-f0-9]{64}$" },
944
+ state: controlStateSchema
810
945
  },
811
- required: ["loopId", "action", "controlId", "requestedAt", "ok", "summary", "requestedAction", "state"]
946
+ required: [
947
+ "loopId",
948
+ "action",
949
+ "controlId",
950
+ "requestedAt",
951
+ "ok",
952
+ "summary",
953
+ "requestedAction",
954
+ "publicationState",
955
+ "applicationState",
956
+ "authorityBoundary",
957
+ "principal",
958
+ "receiptHash",
959
+ "state"
960
+ ]
812
961
  };
813
962
  const evalOutputSchema = {
814
963
  type: "object",
@@ -1207,7 +1356,7 @@ export function createMartinMcpServer(serverInfo) {
1207
1356
  },
1208
1357
  {
1209
1358
  name: "martin_logs",
1210
- description: "Read recent MartinLoop events, ledger entries, and operator control receipts for a single run. " +
1359
+ description: "Read recent MartinLoop events, ledger entries, operator control receipts, and reconciled executor control-application records for a single run. " +
1211
1360
  "Use to observe live or completed run activity, diagnose stuck or failed runs, or audit operator actions. " +
1212
1361
  "Do not use to check run completion status — use martin_get_status instead. " +
1213
1362
  "Do not use to retrieve verification evidence — use martin_get_verification_results instead. " +
@@ -1233,7 +1382,7 @@ export function createMartinMcpServer(serverInfo) {
1233
1382
  },
1234
1383
  {
1235
1384
  name: "martin_pause",
1236
- description: "Write a durable pause receipt for one canonical MartinLoop run. Provide exactly one selector: file, loopId, or latest; runsDir changes the run-store root, while reason and requestedBy add audit context. Use for a temporary hold before risky follow-up work. This records a request and does not kill a process; use martin_cancel to abandon work or martin_continue to resume.",
1385
+ description: "Request a durable cooperative pause for one active MartinLoop run. The executor applies it at the next safe attempt boundary; it does not suspend or kill an in-flight provider. Provide exactly one selector: file, loopId, or latest. Use martin_status to distinguish requested from applied state; use martin_cancel to abandon work or martin_continue to release the applied pause.",
1237
1386
  annotations: {
1238
1387
  destructiveHint: true,
1239
1388
  idempotentHint: false
@@ -1246,18 +1395,17 @@ export function createMartinMcpServer(serverInfo) {
1246
1395
  loopId: { type: "string", description: "Canonical MartinLoop run identifier. Mutually exclusive with file and latest." },
1247
1396
  runsDir: { type: "string", description: "Optional run-store root override used to resolve loopId or latest." },
1248
1397
  latest: { const: true, description: "When true, targets the latest canonical run. Mutually exclusive with file and loopId." },
1249
- reason: { type: "string", description: "Optional non-empty reason recorded in the pause receipt." },
1250
- requestedBy: { type: "string", description: "Optional human or runtime identity label recorded for audit context." }
1398
+ reason: { type: "string", description: "Optional non-empty reason recorded in the pause receipt." }
1251
1399
  }
1252
1400
  },
1253
1401
  outputSchema: controlOutputSchema
1254
1402
  },
1255
1403
  {
1256
1404
  name: "martin_cancel",
1257
- description: "Write a durable cancellation receipt for one canonical MartinLoop run. Provide exactly one selector: file, loopId, or latest; runsDir changes the run-store root, while reason and requestedBy add audit context. Use when work must be abandoned, not temporarily held. This records a request and does not kill a process; use martin_pause for a reversible hold.",
1405
+ description: "Publish the canonical durable cancellation signal for one active MartinLoop run. The active executor polls this signal, aborts its provider, restores governed workspace state, and records terminal evidence. Use the tool when work must stop; publication is not application proof, so use martin_status to observe terminal state or martin_pause for a reversible safe-boundary hold.",
1258
1406
  annotations: {
1259
1407
  destructiveHint: true,
1260
- idempotentHint: false
1408
+ idempotentHint: true
1261
1409
  },
1262
1410
  inputSchema: {
1263
1411
  type: "object",
@@ -1267,15 +1415,14 @@ export function createMartinMcpServer(serverInfo) {
1267
1415
  loopId: { type: "string", description: "Canonical MartinLoop run identifier. Mutually exclusive with file and latest." },
1268
1416
  runsDir: { type: "string", description: "Optional run-store root override used to resolve loopId or latest." },
1269
1417
  latest: { const: true, description: "When true, targets the latest canonical run. Mutually exclusive with file and loopId." },
1270
- reason: { type: "string", description: "Optional non-empty reason recorded in the cancellation receipt." },
1271
- requestedBy: { type: "string", description: "Optional human or runtime identity label recorded for audit context." }
1418
+ reason: { type: "string", description: "Optional non-empty reason recorded in the cancellation receipt." }
1272
1419
  }
1273
1420
  },
1274
1421
  outputSchema: controlOutputSchema
1275
1422
  },
1276
1423
  {
1277
1424
  name: "martin_continue",
1278
- description: "Record a durable continue or resume request for a Martin run after a human pause or approval checkpoint.",
1425
+ description: "Release the exact pending cooperative pause for one active MartinLoop run. Continue is bound to that pause and does not grant approval, widen safety policy, or resume a terminal approval escalation.",
1279
1426
  annotations: {
1280
1427
  destructiveHint: true,
1281
1428
  idempotentHint: false
@@ -1288,8 +1435,7 @@ export function createMartinMcpServer(serverInfo) {
1288
1435
  loopId: { type: "string" },
1289
1436
  runsDir: { type: "string" },
1290
1437
  latest: { const: true },
1291
- reason: { type: "string" },
1292
- requestedBy: { type: "string" }
1438
+ reason: { type: "string" }
1293
1439
  }
1294
1440
  },
1295
1441
  outputSchema: controlOutputSchema
@@ -1832,17 +1978,26 @@ export function createMartinMcpServer(serverInfo) {
1832
1978
  }
1833
1979
  if (name === "martin_pause") {
1834
1980
  const input = validateToolInput("martin_pause", args);
1835
- const output = await createRunControlReceipt("pause", input);
1981
+ const output = await createRunControlReceipt("pause", {
1982
+ ...input,
1983
+ authority: { transport: serverInfo?.controlTransport ?? "untrusted_remote" }
1984
+ });
1836
1985
  return createToolSuccessResult(output, output.summary);
1837
1986
  }
1838
1987
  if (name === "martin_cancel") {
1839
1988
  const input = validateToolInput("martin_cancel", args);
1840
- const output = await createRunControlReceipt("cancel", input);
1989
+ const output = await createRunControlReceipt("cancel", {
1990
+ ...input,
1991
+ authority: { transport: serverInfo?.controlTransport ?? "untrusted_remote" }
1992
+ });
1841
1993
  return createToolSuccessResult(output, output.summary);
1842
1994
  }
1843
1995
  if (name === "martin_continue") {
1844
1996
  const input = validateToolInput("martin_continue", args);
1845
- const output = await createRunControlReceipt("continue", input);
1997
+ const output = await createRunControlReceipt("continue", {
1998
+ ...input,
1999
+ authority: { transport: serverInfo?.controlTransport ?? "untrusted_remote" }
2000
+ });
1846
2001
  return createToolSuccessResult(output, output.summary);
1847
2002
  }
1848
2003
  if (name === "martin_list_runs") {
@@ -1967,6 +2122,7 @@ function normalizeRunBudget(input) {
1967
2122
  }
1968
2123
  export async function connectMartinMcpStdioServer(options = {}) {
1969
2124
  const server = createMartinMcpServer({
2125
+ controlTransport: "local_stdio",
1970
2126
  ...(options.toolProfile ? { toolProfile: options.toolProfile } : {})
1971
2127
  });
1972
2128
  const transport = new StdioServerTransport();
@@ -2031,6 +2187,7 @@ export async function connectMartinMcpHttpServer(options = {}) {
2031
2187
  const port = options.port ?? 3033;
2032
2188
  const path = options.path ?? "/mcp";
2033
2189
  const mcpServer = createMartinMcpServer({
2190
+ controlTransport: "untrusted_remote",
2034
2191
  ...(options.toolProfile ? { toolProfile: options.toolProfile } : {})
2035
2192
  });
2036
2193
  const transport = new StreamableHTTPServerTransport({
@@ -35,6 +35,9 @@ export interface GetStatusOutput {
35
35
  };
36
36
  live: {
37
37
  phase: string;
38
+ requestedControlState: "active" | "paused" | "cancellation_requested" | "terminal";
39
+ appliedControlState: "active" | "paused" | "cancellation_pending" | "terminal";
40
+ /** Backwards-compatible applied pause state. Never reflects an unapplied request. */
38
41
  pauseState: "active" | "paused" | "cancellation_requested";
39
42
  approvalState: "not_required" | "resume_requested";
40
43
  commandsRun: number;
@@ -8,8 +8,11 @@ export async function getStatusTool(input) {
8
8
  const control = input.loopJson !== undefined
9
9
  ? {
10
10
  requestedState: "active",
11
+ appliedState: "active",
11
12
  approvalState: "not_required",
12
- receipts: []
13
+ receipts: [],
14
+ applications: [],
15
+ publications: []
13
16
  }
14
17
  : await readRunControlState(input);
15
18
  const latestEvent = loop.events?.at(-1);
@@ -52,7 +55,13 @@ export async function getStatusTool(input) {
52
55
  },
53
56
  live: {
54
57
  phase: latestEvent?.lifecycleState ?? loop.lifecycleState,
55
- pauseState: control.requestedState,
58
+ requestedControlState: control.requestedState,
59
+ appliedControlState: control.appliedState,
60
+ pauseState: control.appliedState === "paused"
61
+ ? "paused"
62
+ : control.appliedState === "cancellation_pending"
63
+ ? "cancellation_requested"
64
+ : "active",
56
65
  approvalState: control.approvalState,
57
66
  commandsRun: loop.attempts.length,
58
67
  filesTouched: changedFiles,
@@ -12,6 +12,8 @@ export interface MartinLogsOutput {
12
12
  logCount: number;
13
13
  live: {
14
14
  lifecycleState: string;
15
+ requestedControlState: "active" | "paused" | "cancellation_requested" | "terminal";
16
+ appliedControlState: "active" | "paused" | "cancellation_pending" | "terminal";
15
17
  pauseState: "active" | "paused" | "cancellation_requested";
16
18
  approvalState: "not_required" | "resume_requested";
17
19
  };
@@ -23,11 +23,27 @@ export async function martinLogsTool(input) {
23
23
  kind: `run.${receipt.action}`,
24
24
  payload: {
25
25
  controlId: receipt.controlId,
26
+ ...(receipt.expectedPreviousControlId
27
+ ? { expectedPreviousControlId: receipt.expectedPreviousControlId }
28
+ : {}),
26
29
  ...(receipt.reason ? { reason: receipt.reason } : {}),
27
- ...(receipt.requestedBy ? { requestedBy: receipt.requestedBy } : {})
30
+ principal: receipt.principal,
31
+ authorityBoundary: receipt.authorityBoundary,
32
+ receiptHash: receipt.receiptHash
28
33
  }
29
34
  }));
30
- const entries = [...eventEntries, ...ledgerEntries, ...controlEntries]
35
+ const applicationEntries = controls.applications.map((application) => ({
36
+ timestamp: application.appliedAt,
37
+ source: "control",
38
+ kind: `run.${application.action}`,
39
+ payload: {
40
+ controlId: application.controlId,
41
+ ...(application.relatedControlId
42
+ ? { relatedControlId: application.relatedControlId }
43
+ : {})
44
+ }
45
+ }));
46
+ const entries = [...eventEntries, ...ledgerEntries, ...controlEntries, ...applicationEntries]
31
47
  .sort((left, right) => {
32
48
  const leftTime = left.timestamp ? new Date(left.timestamp).getTime() : 0;
33
49
  const rightTime = right.timestamp ? new Date(right.timestamp).getTime() : 0;
@@ -41,7 +57,13 @@ export async function martinLogsTool(input) {
41
57
  logCount: entries.length,
42
58
  live: {
43
59
  lifecycleState: detail.loop.lifecycleState,
44
- pauseState: controls.requestedState,
60
+ requestedControlState: controls.requestedState,
61
+ appliedControlState: controls.appliedState,
62
+ pauseState: controls.appliedState === "paused"
63
+ ? "paused"
64
+ : controls.appliedState === "cancellation_pending"
65
+ ? "cancellation_requested"
66
+ : "active",
45
67
  approvalState: controls.approvalState
46
68
  },
47
69
  entries
@@ -1,3 +1,4 @@
1
+ import { type RunControlApplication, type RunControlSnapshot, type RunControlSource } from "../vendor/core/index.js";
1
2
  import { type DetailedLoopSource } from "./run-store.js";
2
3
  export type MartinControlAction = "pause" | "cancel" | "continue";
3
4
  export interface MartinRunControlRequestInput {
@@ -6,22 +7,52 @@ export interface MartinRunControlRequestInput {
6
7
  runsDir?: string;
7
8
  latest?: boolean;
8
9
  reason?: string;
9
- requestedBy?: string;
10
+ /** Injected by the server; never accepted from MCP tool arguments. */
11
+ authority?: {
12
+ transport: "local_stdio" | "untrusted_remote";
13
+ };
10
14
  }
11
15
  export interface MartinRunControlReceipt {
16
+ schemaVersion: "martin.run-control.v2";
12
17
  controlId: string;
13
18
  loopId: string;
14
19
  action: MartinControlAction;
15
20
  requestedAt: string;
21
+ authorityBoundary: "local_os_user";
22
+ principal: "local_mcp_client";
23
+ executorLeaseToken: string;
24
+ workspaceId?: string;
25
+ projectId?: string;
26
+ expectedPreviousControlId?: string;
16
27
  reason?: string;
17
- requestedBy?: string;
28
+ previousReceiptHash?: string;
29
+ receiptHash: string;
30
+ }
31
+ interface RunControlPublication {
32
+ schemaVersion: "martin.run-control-publication.v1";
33
+ loopId: string;
34
+ controlId: string;
35
+ receiptHash: string;
36
+ signalState: "created" | "already_exists";
37
+ recordedAt: string;
38
+ previousPublicationHash?: string;
39
+ publicationHash: string;
40
+ }
41
+ interface StoredRunControlApplication extends RunControlApplication {
42
+ schemaVersion: "martin.run-control-application.v2";
43
+ previousApplicationHash?: string;
44
+ applicationHash: string;
18
45
  }
19
46
  export interface MartinRunControlState {
20
- requestedState: "active" | "paused" | "cancellation_requested";
47
+ requestedState: "active" | "paused" | "cancellation_requested" | "terminal";
21
48
  latestReceipt?: MartinRunControlReceipt;
22
49
  approvalState: "not_required" | "resume_requested";
50
+ appliedState: "active" | "paused" | "cancellation_pending" | "terminal";
23
51
  receipts: MartinRunControlReceipt[];
52
+ applications: StoredRunControlApplication[];
53
+ publications: RunControlPublication[];
24
54
  receiptPath?: string;
55
+ applicationPath?: string;
25
56
  }
26
57
  export interface MartinRunControlOutput {
27
58
  ok: boolean;
@@ -32,10 +63,24 @@ export interface MartinRunControlOutput {
32
63
  action: MartinControlAction;
33
64
  controlId: string;
34
65
  requestedAt: string;
66
+ publicationState: "published" | "already_requested" | "receipt_recorded" | "published_audit_pending";
67
+ applicationState: "pending_observation" | "requested_only" | "terminal_reconciled";
35
68
  reason?: string;
36
- requestedBy?: string;
69
+ authorityBoundary: "local_os_user";
70
+ principal: "local_mcp_client";
71
+ receiptHash: string;
37
72
  }
38
- export declare function createRunControlReceipt(action: MartinControlAction, input: MartinRunControlRequestInput): Promise<MartinRunControlOutput>;
73
+ export declare function createRunControlReceipt(action: MartinControlAction, input: MartinRunControlRequestInput, testHooks?: {
74
+ beforeLockedReload?: () => Promise<void>;
75
+ afterJournalOpen?: (journalPath: string) => Promise<void>;
76
+ lockMaxRetries?: number;
77
+ }): Promise<MartinRunControlOutput>;
39
78
  export declare function readRunControlState(detailOrInput: DetailedLoopSource | MartinRunControlRequestInput): Promise<MartinRunControlState>;
40
- export declare function readControlReceipts(receiptPath: string): Promise<MartinRunControlReceipt[]>;
79
+ export declare function createFileRunControlSource(runsRoot: string): RunControlSource;
80
+ export declare function reconcileRunControlApplications(receipts: readonly MartinRunControlReceipt[], applications: readonly StoredRunControlApplication[]): "active" | "paused";
81
+ export declare function resolveRunControlSnapshot(receipts: readonly MartinRunControlReceipt[]): RunControlSnapshot;
82
+ export declare function readRunControlApplications(applicationPath: string, expectedLoopId?: string): Promise<StoredRunControlApplication[]>;
83
+ export declare function readControlReceipts(receiptPath: string, expectedLoopId?: string): Promise<MartinRunControlReceipt[]>;
84
+ export declare function hashCanonical(value: unknown): string;
41
85
  export declare function validateControlReason(value?: string): string | undefined;
86
+ export {};