forge-workflow 0.1.0-beta.6 → 0.1.0-beta.8

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 (179) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/bin/forge-gh-proxy.js +15 -0
  3. package/bin/forge-github-credential.js +21 -0
  4. package/bin/forge.js +16 -1
  5. package/docs/guides/MIGRATION.md +4 -3
  6. package/docs/reference/RELEASE.md +5 -3
  7. package/docs/reference/VALIDATION.md +14 -0
  8. package/docs/reference/github-accounts.md +114 -38
  9. package/lib/bun-workflow-pins.js +129 -3
  10. package/lib/commands/github.js +234 -49
  11. package/lib/commands/merge.js +2 -2
  12. package/lib/commands/push.js +308 -47
  13. package/lib/commands/release.js +18 -1
  14. package/lib/commands/shepherd.js +1 -1
  15. package/lib/commands/test.js +8 -0
  16. package/lib/commands/validate.js +219 -67
  17. package/lib/gh-proxy.js +309 -0
  18. package/lib/github-context.js +160 -25
  19. package/lib/github-credential.js +47 -0
  20. package/lib/github-router.js +774 -0
  21. package/lib/kernel/migrations.js +1 -1
  22. package/lib/kernel/sqlite-driver.js +1 -1
  23. package/lib/memory/router.js +1 -1
  24. package/lib/memory/usage-evidence.js +1 -1
  25. package/lib/native-gh.js +177 -0
  26. package/lib/npm-publish-workflow.js +5 -5
  27. package/lib/pr-monitor/flow-monitor.js +127 -28
  28. package/lib/pr-monitor/reconcile-executor.js +1 -1
  29. package/lib/project-memory.js +1 -1
  30. package/lib/protected-state-authority.js +214 -13
  31. package/lib/release-readiness.js +57 -41
  32. package/lib/shell-utils.js +41 -15
  33. package/lib/test-workflow.js +389 -0
  34. package/lib/validation-receipt.js +305 -0
  35. package/lib/workflow-templates/test.yml +654 -0
  36. package/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
  37. package/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
  38. package/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
  39. package/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
  40. package/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
  41. package/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
  42. package/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
  43. package/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
  44. package/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
  45. package/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
  46. package/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
  47. package/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
  48. package/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
  49. package/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
  50. package/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
  51. package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
  52. package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
  53. package/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
  54. package/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
  55. package/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
  56. package/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
  57. package/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
  58. package/node_modules/@forge/contracts/index.js +32 -0
  59. package/node_modules/@forge/contracts/package.json +35 -0
  60. package/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
  61. package/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
  62. package/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
  63. package/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
  64. package/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
  65. package/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
  66. package/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
  67. package/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
  68. package/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
  69. package/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
  70. package/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
  71. package/node_modules/@forge/contracts/src/baseline.js +23 -0
  72. package/node_modules/@forge/contracts/src/canonical.js +151 -0
  73. package/node_modules/@forge/contracts/src/definitions.js +176 -0
  74. package/node_modules/@forge/contracts/src/identity.js +42 -0
  75. package/node_modules/@forge/contracts/src/schema.js +72 -0
  76. package/node_modules/@forge/contracts/src/validate.js +305 -0
  77. package/node_modules/@forge/flow/index.js +114 -0
  78. package/node_modules/@forge/flow/package.json +34 -0
  79. package/node_modules/@forge/flow/src/bounded-loop.js +415 -0
  80. package/node_modules/@forge/flow/src/efficiency-supervisor.js +89 -0
  81. package/node_modules/@forge/flow/src/executor.js +279 -0
  82. package/node_modules/@forge/flow/src/monitor-durability.js +419 -0
  83. package/node_modules/@forge/flow/src/monitor-runtime.js +460 -0
  84. package/node_modules/@forge/flow/src/process-lifecycle.js +469 -0
  85. package/node_modules/@forge/flow/src/skill-runtime.js +343 -0
  86. package/node_modules/@forge/memory/index.js +331 -0
  87. package/node_modules/@forge/memory/package.json +34 -0
  88. package/node_modules/@forge/memory/src/authority-provider.js +67 -0
  89. package/node_modules/@forge/memory/src/backend-registry.js +166 -0
  90. package/node_modules/@forge/memory/src/feedback-intake.js +243 -0
  91. package/node_modules/@forge/memory/src/pr-lifecycle-authority.js +946 -0
  92. package/node_modules/@forge/memory/src/usage-evidence.js +205 -0
  93. package/package.json +12 -2
  94. package/packages/flow/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
  95. package/packages/flow/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
  96. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
  97. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
  98. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
  99. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
  100. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
  101. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
  102. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
  103. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
  104. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
  105. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
  106. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
  107. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
  108. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
  109. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
  110. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
  111. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
  112. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
  113. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
  114. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
  115. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
  116. package/packages/flow/node_modules/@forge/contracts/index.js +32 -0
  117. package/packages/flow/node_modules/@forge/contracts/package.json +35 -0
  118. package/packages/flow/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
  119. package/packages/flow/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
  120. package/packages/flow/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
  121. package/packages/flow/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
  122. package/packages/flow/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
  123. package/packages/flow/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
  124. package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
  125. package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
  126. package/packages/flow/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
  127. package/packages/flow/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
  128. package/packages/flow/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
  129. package/packages/flow/node_modules/@forge/contracts/src/baseline.js +23 -0
  130. package/packages/flow/node_modules/@forge/contracts/src/canonical.js +151 -0
  131. package/packages/flow/node_modules/@forge/contracts/src/definitions.js +176 -0
  132. package/packages/flow/node_modules/@forge/contracts/src/identity.js +42 -0
  133. package/packages/flow/node_modules/@forge/contracts/src/schema.js +72 -0
  134. package/packages/flow/node_modules/@forge/contracts/src/validate.js +305 -0
  135. package/packages/memory/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
  136. package/packages/memory/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
  137. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
  138. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
  139. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
  140. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
  141. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
  142. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
  143. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
  144. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
  145. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
  146. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
  147. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
  148. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
  149. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
  150. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
  151. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
  152. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
  153. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
  154. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
  155. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
  156. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
  157. package/packages/memory/node_modules/@forge/contracts/index.js +32 -0
  158. package/packages/memory/node_modules/@forge/contracts/package.json +35 -0
  159. package/packages/memory/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
  160. package/packages/memory/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
  161. package/packages/memory/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
  162. package/packages/memory/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
  163. package/packages/memory/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
  164. package/packages/memory/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
  165. package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
  166. package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
  167. package/packages/memory/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
  168. package/packages/memory/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
  169. package/packages/memory/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
  170. package/packages/memory/node_modules/@forge/contracts/src/baseline.js +23 -0
  171. package/packages/memory/node_modules/@forge/contracts/src/canonical.js +151 -0
  172. package/packages/memory/node_modules/@forge/contracts/src/definitions.js +176 -0
  173. package/packages/memory/node_modules/@forge/contracts/src/identity.js +42 -0
  174. package/packages/memory/node_modules/@forge/contracts/src/schema.js +72 -0
  175. package/packages/memory/node_modules/@forge/contracts/src/validate.js +305 -0
  176. package/scripts/check-forge-token.js +85 -67
  177. package/scripts/gen-embedded-assets.mjs +31 -5
  178. package/scripts/test-full-suite.js +69 -15
  179. package/scripts/test.js +11 -1
@@ -1,5 +1,5 @@
1
1
  const { getKernelSchema, validateKernelSchema } = require('./schema');
2
- const { USAGE_EVIDENCE_MIGRATION } = require('../../packages/memory');
2
+ const { USAGE_EVIDENCE_MIGRATION } = require('@forge/memory');
3
3
 
4
4
  function assertIdentifier(identifier, label) {
5
5
  if (!/^[a-z][a-z0-9_]*$/.test(identifier)) {
@@ -30,7 +30,7 @@ const {
30
30
  const { CONFLICT_SIGNAL, classifyConflictSignal } = require('./conflict-signal');
31
31
  const { normalizeRecallHit } = require('../memory-recall');
32
32
  const { getPackageRoot } = require('../package-root');
33
- const { appendUsageEvidence, rebuildUsageProjection } = require('../../packages/memory');
33
+ const { appendUsageEvidence, rebuildUsageProjection } = require('@forge/memory');
34
34
 
35
35
  const BUILTIN_SQLITE_RUNTIME_ORDER = Object.freeze(['bun:sqlite', 'node:sqlite']);
36
36
  let probeCounter = 0;
@@ -32,7 +32,7 @@ const projectMemory = require('../project-memory');
32
32
  const {
33
33
  assertBackend,
34
34
  createMemoryBackendRegistry,
35
- } = require('../../packages/memory');
35
+ } = require('@forge/memory');
36
36
 
37
37
  /** Supported PUBLIC backends, default first. */
38
38
  const MEMORY_BACKENDS = ['local', 'graphiti'];
@@ -1,4 +1,4 @@
1
1
  'use strict';
2
2
 
3
3
  // Compatibility shim: the durable usage contract belongs to @forge/memory.
4
- module.exports = require('../../packages/memory');
4
+ module.exports = require('@forge/memory');
@@ -0,0 +1,177 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const os = require('node:os');
6
+ const { spawnSync } = require('node:child_process');
7
+ const { isCompiledBinary } = require('./package-root');
8
+
9
+ const ROUTER_MARKER = 'forge-gh-router-v1';
10
+ const LOCAL_COMMANDS = new Set([
11
+ 'accessibility', 'actions', 'alias', 'auth', 'completion', 'config', 'environment', 'exit-codes', 'formatting',
12
+ 'help', 'licenses', 'mintty', 'preview', 'reference', 'telemetry', 'version',
13
+ ]);
14
+ const HELP_COMMANDS = new Set([
15
+ 'agent-task', 'api', 'attestation', 'browse', 'cache', 'co', 'codespace', 'copilot', 'discussion', 'extension',
16
+ 'gist', 'gpg-key', 'issue', 'label', 'org', 'pr', 'project', 'release', 'repo', 'ruleset', 'run', 'search',
17
+ 'secret', 'skill', 'skills', 'ssh-key', 'status', 'variable', 'workflow',
18
+ ]);
19
+ const GLOBAL_VALUE_FLAGS = new Set(['--hostname', '--repo', '-R']);
20
+ const SKILL_VALUE_FLAGS = new Set(['--agent', '--dir', '--pin', '--scope']);
21
+ const TRUE_BOOLEAN_VALUES = new Set(['1', 't', 'T', 'TRUE', 'true', 'True']);
22
+
23
+ function environmentValue(environment, name) {
24
+ const key = Object.keys(environment).find(candidate => candidate.toUpperCase() === name);
25
+ return key ? environment[key] : undefined;
26
+ }
27
+
28
+ function withoutProxyMarker(environment) {
29
+ return Object.fromEntries(Object.entries(environment).filter(([key]) => key.toUpperCase() !== 'FORGE_GH_PROXY_ACTIVE'));
30
+ }
31
+
32
+ function commandName(args) {
33
+ for (let index = 0; index < args.length; index += 1) {
34
+ const arg = args[index];
35
+ if (GLOBAL_VALUE_FLAGS.has(arg)) { index += 1; continue; }
36
+ if (!arg.startsWith('-')) return arg;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ function enabledBooleanFlag(argument, name) {
42
+ return argument === name || (argument.startsWith(`${name}=`)
43
+ && TRUE_BOOLEAN_VALUES.has(argument.slice(name.length + 1)));
44
+ }
45
+
46
+ function shortOptionValue(arg, resolveOptionValue) {
47
+ for (let offset = 1; offset < arg.length; offset += 1) {
48
+ const flag = `-${arg[offset]}`;
49
+ const takesValue = resolveOptionValue(flag);
50
+ if (typeof takesValue !== 'boolean') throw new Error(`Unable to determine GitHub CLI option arity for ${flag}.`);
51
+ if (takesValue) return { flag, attached: arg.slice(offset + 1).replace(/^=/, '') };
52
+ }
53
+ return null;
54
+ }
55
+
56
+ function isLocalSkillInvocation(args, command) {
57
+ const commandIndex = args.indexOf(command);
58
+ const subcommand = args[commandIndex + 1];
59
+ if (subcommand === 'list') return true;
60
+ if (!['install', 'add'].includes(subcommand)) return false;
61
+ let local = false;
62
+ for (let index = commandIndex + 2; index < args.length; index += 1) {
63
+ const arg = args[index];
64
+ if (arg === '--') return false;
65
+ if (SKILL_VALUE_FLAGS.has(arg)) { index += 1; continue; }
66
+ if (enabledBooleanFlag(arg, '--upstream')) return false;
67
+ if (enabledBooleanFlag(arg, '--from-local')) local = true;
68
+ }
69
+ return local;
70
+ }
71
+
72
+ function isLocalExtensionInvocation(args, command) {
73
+ const commandIndex = args.indexOf(command);
74
+ if (args[commandIndex + 1] !== 'install') return false;
75
+ const values = [];
76
+ for (let index = commandIndex + 2; index < args.length; index += 1) {
77
+ const arg = args[index];
78
+ if (arg === '--') return false;
79
+ if (arg === '--pin') { index += 1; continue; }
80
+ if (arg === '--force' || arg.startsWith('--force=') || arg.startsWith('--pin=')) continue;
81
+ if (arg.startsWith('-')) return false;
82
+ values.push(arg);
83
+ }
84
+ return values.length === 1 && values[0] === '.';
85
+ }
86
+
87
+ function isLocalGhInvocation(args, resolveOptionValue = () => null) {
88
+ if (args.length === 0) return true;
89
+ const command = commandName(args);
90
+ if (LOCAL_COMMANDS.has(command)) return true;
91
+ if (['skill', 'skills'].includes(command) && isLocalSkillInvocation(args, command)) return true;
92
+ if (command === 'extension' && isLocalExtensionInvocation(args, command)) return true;
93
+ const help = argument => {
94
+ if (enabledBooleanFlag(argument, '--help')) return true;
95
+ if (!enabledBooleanFlag(argument, '-h')) return false;
96
+ try { return resolveOptionValue('-h') === false; } catch { return false; }
97
+ };
98
+ if (args.length === 1) return enabledBooleanFlag(args[0], '--help') || enabledBooleanFlag(args[0], '-h')
99
+ || enabledBooleanFlag(args[0], '--version');
100
+ const delimiter = args.indexOf('--');
101
+ const end = delimiter < 0 ? args.length : delimiter;
102
+ if (!HELP_COMMANDS.has(command) || !args.slice(0, end).some(help)) return false;
103
+ for (let index = 0; index < end; index += 1) {
104
+ const arg = args[index];
105
+ if (help(arg)) return true;
106
+ if (!arg.startsWith('-') || arg.includes('=')) continue;
107
+ try {
108
+ if (arg.startsWith('--')) {
109
+ const takesValue = GLOBAL_VALUE_FLAGS.has(arg) || resolveOptionValue(arg);
110
+ if (typeof takesValue !== 'boolean') return false;
111
+ if (takesValue) index += 1;
112
+ } else {
113
+ const option = shortOptionValue(arg, resolveOptionValue);
114
+ if (option && !option.attached) index += 1;
115
+ }
116
+ } catch { return false; }
117
+ }
118
+ return false;
119
+ }
120
+
121
+ function exitCode(result) {
122
+ if (result?.error?.code === 'ENOENT') return 127;
123
+ if (result?.error?.code === 'EACCES') return 126;
124
+ if (Number.isInteger(result?.status)) return result.status;
125
+ return result?.signal && os.constants.signals[result.signal] ? 128 + os.constants.signals[result.signal] : 1;
126
+ }
127
+
128
+ function isForgeProxy(candidate, ownPath) {
129
+ const candidatePath = fs.realpathSync(candidate);
130
+ try {
131
+ if (candidatePath === fs.realpathSync(ownPath)) return true;
132
+ } catch { /* compiled Bun entrypoints may not exist on the host filesystem */ }
133
+ const stat = fs.statSync(candidatePath);
134
+ if (!stat.isFile()) throw new Error('GitHub CLI candidate is not a file.');
135
+ if (stat.size > 64 * 1024) return false;
136
+ const text = fs.readFileSync(candidatePath, 'utf8').replaceAll('\\', '/').toLowerCase();
137
+ return text.includes(ROUTER_MARKER) || (text.includes('forge-workflow') && text.includes('/bin/gh.js'));
138
+ }
139
+
140
+ function resolveRealGh(options = {}) {
141
+ const platform = options.platform || process.platform;
142
+ const environment = options.env || process.env;
143
+ const pathValue = options.pathEnv ?? environmentValue(environment, 'PATH') ?? '';
144
+ const ownPath = options.ownPath ?? (isCompiledBinary() ? process.execPath : process.argv[1]);
145
+ const extensions = platform === 'win32'
146
+ ? (options.pathExt || environmentValue(environment, 'PATHEXT') || '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean)
147
+ : [''];
148
+ for (const directory of pathValue.split(path.delimiter).filter(Boolean)) {
149
+ for (const extension of extensions) {
150
+ const candidate = path.join(directory, `gh${extension.toLowerCase()}`);
151
+ try {
152
+ fs.accessSync(candidate, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
153
+ if (!isForgeProxy(candidate, ownPath)) return candidate;
154
+ } catch { /* keep searching */ }
155
+ }
156
+ }
157
+ return null;
158
+ }
159
+
160
+ function runNativeGh(args, projectRoot = process.cwd(), options = {}) {
161
+ const writeError = options.writeError || (value => process.stderr.write(value));
162
+ const executable = (options.resolveExecutable || resolveRealGh)(options);
163
+ if (!executable) {
164
+ writeError('GitHub CLI executable not found outside the Forge proxy.\n');
165
+ return 127;
166
+ }
167
+ return exitCode((options.spawnSync || spawnSync)(executable, args, {
168
+ cwd: projectRoot,
169
+ env: withoutProxyMarker(options.baseEnv || process.env),
170
+ stdio: 'inherit',
171
+ shell: false,
172
+ }));
173
+ }
174
+
175
+ module.exports = {
176
+ commandName, exitCode, isForgeProxy, isLocalGhInvocation, resolveRealGh, runNativeGh, shortOptionValue, withoutProxyMarker,
177
+ };
@@ -12,11 +12,11 @@ const {
12
12
  } = require('./protected-state-surfaces');
13
13
  const {
14
14
  completeNpmPublishWorkflowAuthorization,
15
+ isValidGitObjectId,
15
16
  issueNpmPublishWorkflowAuthorization,
16
17
  } = require('./protected-state-authority');
17
18
 
18
19
  const NPM_PUBLISH_WORKFLOW_PATH = '.github/workflows/npm-publish.yml';
19
- const FULL_COMMIT_SHA = /^[0-9a-f]{40}$/;
20
20
  const BUN_VERSION = /^bun@(\d+\.\d+\.\d+)$/.exec(require('../package.json').packageManager)?.[1];
21
21
  if (!BUN_VERSION) throw new Error('package.json must pin an exact stable Bun version.');
22
22
 
@@ -64,8 +64,8 @@ function verifyExpectedHead(projectRoot, expectedHead, resolveHead = resolveCurr
64
64
  if (!expectedHead) {
65
65
  return { success: false, error: '--expect-head is required for protected workflow generation.' };
66
66
  }
67
- if (!FULL_COMMIT_SHA.test(expectedHead)) {
68
- return { success: false, error: '--expect-head must be a full 40-character lowercase commit SHA.' };
67
+ if (!isValidGitObjectId(expectedHead)) {
68
+ return { success: false, error: '--expect-head must be a full 40- or 64-character lowercase commit SHA.' };
69
69
  }
70
70
 
71
71
  let currentHead;
@@ -74,8 +74,8 @@ function verifyExpectedHead(projectRoot, expectedHead, resolveHead = resolveCurr
74
74
  } catch (error) {
75
75
  return { success: false, error: `Could not resolve current HEAD: ${error.message}` };
76
76
  }
77
- if (!FULL_COMMIT_SHA.test(currentHead)) {
78
- return { success: false, error: 'Current HEAD did not resolve to a full 40-character lowercase commit SHA.' };
77
+ if (!isValidGitObjectId(currentHead)) {
78
+ return { success: false, error: 'Current HEAD did not resolve to a full 40- or 64-character lowercase commit SHA.' };
79
79
  }
80
80
  if (currentHead !== expectedHead) {
81
81
  return {
@@ -3,7 +3,7 @@
3
3
  const crypto = require('node:crypto');
4
4
  const zlib = require('node:zlib');
5
5
 
6
- const { computeContentHash, validateContractStructure } = require('../../packages/memory-contracts');
6
+ const { computeContentHash, validateContractStructure } = require('@forge/contracts');
7
7
  const {
8
8
  MonitorRuntimeError,
9
9
  createMonitorDurabilityBridge,
@@ -11,7 +11,7 @@ const {
11
11
  createMonitorState,
12
12
  reduceMonitor,
13
13
  reduceMonitorBatch,
14
- } = require('../../packages/flow');
14
+ } = require('@forge/flow');
15
15
  const { diffSnapshots } = require('./differ');
16
16
  const { EVENT_TYPES: T, makeEvent } = require('./events');
17
17
  const { finalizeRecords, MAX_EVENT_DELTAS } = require('./monitor');
@@ -139,7 +139,7 @@ function durableMonitorRecord(record) {
139
139
  };
140
140
  }
141
141
 
142
- function encodePendingBatch(ctx, subjectRevision, records, snapshot) {
142
+ function encodeBoundedPendingBatch(ctx, subjectRevision, records, snapshot) {
143
143
  if (records.length < 1 || records.length > PENDING_BATCH_RECORD_LIMIT) {
144
144
  throw new FlowMonitorError('INVALID_OBSERVATION', 'Pending monitor transition batch exceeds its record bound');
145
145
  }
@@ -147,7 +147,7 @@ function encodePendingBatch(ctx, subjectRevision, records, snapshot) {
147
147
  monitor_id: ctx.monitorId,
148
148
  subject_revision: subjectRevision,
149
149
  snapshot_identity: snapshot === null ? null : snapshotIdentity(snapshot),
150
- records: records.map(durableMonitorRecord),
150
+ records,
151
151
  snapshot,
152
152
  };
153
153
  const serialized = JSON.stringify({
@@ -164,6 +164,18 @@ function encodePendingBatch(ctx, subjectRevision, records, snapshot) {
164
164
  return encoded;
165
165
  }
166
166
 
167
+ function encodePendingBatch(ctx, subjectRevision, records, snapshot) {
168
+ if (records.length < 1 || records.length > PENDING_BATCH_RECORD_LIMIT) {
169
+ throw new FlowMonitorError('INVALID_OBSERVATION', 'Pending monitor transition batch exceeds its record bound');
170
+ }
171
+ return encodeBoundedPendingBatch(
172
+ ctx,
173
+ subjectRevision,
174
+ records.map(durableMonitorRecord),
175
+ snapshot,
176
+ );
177
+ }
178
+
167
179
  function decodePendingBatch(value, ctx, subjectRevision) {
168
180
  if (typeof value !== 'string' || value.length > PENDING_BATCH_LIMIT) {
169
181
  throw new FlowMonitorError('MONITOR_HISTORY_INCOMPLETE', 'Pending monitor transition batch is missing');
@@ -196,41 +208,126 @@ function segmentablePendingBatchError(error) {
196
208
  ].includes(error.message);
197
209
  }
198
210
 
199
- function splitPendingBatches(ctx, subjectRevision, records, snapshot) {
200
- const batches = [];
201
- let offset = 0;
202
- while (offset < records.length) {
211
+ function findAdaptiveBatchBoundary(maxCount, preferredCount, encode) {
212
+ if (!Number.isSafeInteger(maxCount) || maxCount < 1) return null;
213
+ let accepted = null;
214
+ const attempt = (count) => {
203
215
  try {
204
- const encoded = encodePendingBatch(ctx, subjectRevision, records.slice(offset), snapshot);
205
- appendPendingBatch(batches, { encoded, records: records.slice(offset), snapshot });
206
- break;
216
+ accepted = { count, encoded: encode(count) };
217
+ return true;
207
218
  } catch (error) {
208
219
  if (!segmentablePendingBatchError(error)) throw error;
220
+ return false;
209
221
  }
210
-
222
+ };
223
+ const initialCount = Math.min(maxCount, Math.max(1, preferredCount || 1));
224
+ if (!attempt(initialCount)) {
211
225
  let low = 1;
212
- let high = records.length - offset - 1;
213
- let accepted = null;
226
+ let high = initialCount - 1;
214
227
  while (low <= high) {
215
228
  const count = Math.floor((low + high) / 2);
216
- const candidate = records.slice(offset, offset + count);
217
- try {
218
- accepted = {
219
- count,
220
- encoded: encodePendingBatch(ctx, subjectRevision, candidate, null),
221
- records: candidate,
222
- snapshot: null,
223
- };
224
- low = count + 1;
225
- } catch (error) {
226
- if (!segmentablePendingBatchError(error)) throw error;
227
- high = count - 1;
228
- }
229
+ if (attempt(count)) low = count + 1;
230
+ else high = count - 1;
231
+ }
232
+ return accepted;
233
+ }
234
+
235
+ let low = initialCount + 1;
236
+ let probe = Math.min(maxCount, Math.max(low, initialCount * 2));
237
+ while (low <= maxCount) {
238
+ if (attempt(probe)) {
239
+ low = probe + 1;
240
+ if (probe === maxCount) break;
241
+ probe = Math.min(maxCount, Math.max(low, probe * 2));
242
+ continue;
243
+ }
244
+ let high = probe - 1;
245
+ while (low <= high) {
246
+ const count = Math.floor((low + high) / 2);
247
+ if (attempt(count)) low = count + 1;
248
+ else high = count - 1;
249
+ }
250
+ break;
251
+ }
252
+ return accepted;
253
+ }
254
+
255
+ function splitPendingBatches(
256
+ ctx,
257
+ subjectRevision,
258
+ records,
259
+ snapshot,
260
+ encodeBatch = encodeBoundedPendingBatch,
261
+ ) {
262
+ const batches = [];
263
+ if (records.length === 0) return batches;
264
+ if (records.length > PENDING_BATCH_RECORD_LIMIT) {
265
+ throw new FlowMonitorError('INVALID_OBSERVATION', 'Pending monitor transition batch exceeds its record bound');
266
+ }
267
+ const boundedRecords = records.map(durableMonitorRecord);
268
+ let offset = 0;
269
+ let preferredCount = 1;
270
+ let failedTerminalCount = null;
271
+ const appendTerminalBatch = (start) => {
272
+ const remainingCount = records.length - start;
273
+ try {
274
+ const encoded = encodeBatch(ctx, subjectRevision, boundedRecords.slice(start), snapshot);
275
+ appendPendingBatch(batches, { encoded, records: records.slice(start), snapshot });
276
+ return true;
277
+ } catch (error) {
278
+ if (!segmentablePendingBatchError(error)) throw error;
279
+ failedTerminalCount = remainingCount;
280
+ return false;
281
+ }
282
+ };
283
+
284
+ if (appendTerminalBatch(0)) return batches;
285
+
286
+ while (offset < records.length) {
287
+ const remainingCount = records.length - offset;
288
+ let accepted = findAdaptiveBatchBoundary(
289
+ remainingCount,
290
+ preferredCount,
291
+ count => encodeBatch(
292
+ ctx,
293
+ subjectRevision,
294
+ boundedRecords.slice(offset, offset + count),
295
+ null,
296
+ ),
297
+ );
298
+ if (!accepted && failedTerminalCount !== remainingCount && appendTerminalBatch(offset)) break;
299
+ if (accepted?.count === remainingCount && failedTerminalCount !== remainingCount) {
300
+ if (appendTerminalBatch(offset)) break;
301
+ }
302
+ if (accepted?.count === remainingCount) {
303
+ accepted = findAdaptiveBatchBoundary(
304
+ remainingCount - 1,
305
+ Math.min(preferredCount, remainingCount - 1),
306
+ count => encodeBatch(
307
+ ctx,
308
+ subjectRevision,
309
+ boundedRecords.slice(offset, offset + count),
310
+ null,
311
+ ),
312
+ );
313
+ }
314
+ if (accepted?.count < remainingCount
315
+ && batches.length === BATCH_PLAN_SEGMENT_LIMIT - 1) {
316
+ if (failedTerminalCount !== remainingCount && appendTerminalBatch(offset)) break;
317
+ throw new FlowMonitorError(
318
+ 'INVALID_OBSERVATION',
319
+ 'Pending monitor transition plan exceeds its segment bound',
320
+ );
229
321
  }
230
322
  if (!accepted) {
231
323
  throw new FlowMonitorError('INVALID_OBSERVATION', 'Pending monitor transition record exceeds its durable bound');
232
324
  }
233
- appendPendingBatch(batches, accepted);
325
+ appendPendingBatch(batches, {
326
+ encoded: accepted.encoded,
327
+ records: records.slice(offset, offset + accepted.count),
328
+ snapshot: null,
329
+ });
330
+ preferredCount = accepted.count;
234
331
  offset += accepted.count;
235
332
  }
236
333
  return batches;
@@ -1418,7 +1515,9 @@ module.exports = {
1418
1515
  appendPendingBatch,
1419
1516
  decodePrivateSafeBase64,
1420
1517
  encodePrivateSafeBase64,
1518
+ findAdaptiveBatchBoundary,
1421
1519
  remainingSegmentedRecords,
1520
+ splitPendingBatches,
1422
1521
  validOrphanedPrecommitPlan,
1423
1522
  },
1424
1523
  };
@@ -1081,7 +1081,7 @@ async function migrateLegacyAuthority(projectRoot, opts = {}) {
1081
1081
 
1082
1082
  async function defaultBuildBroker({ projectRoot, gitCommonDir }) {
1083
1083
  const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
1084
- const { createMonitorStore } = require('../../packages/memory');
1084
+ const { createMonitorStore } = require('@forge/memory');
1085
1085
  const deps = await buildMigratedKernelIssueDeps({ projectRoot, gitCommonDir });
1086
1086
  const store = createMonitorStore(deps.kernelDriver);
1087
1087
  return {
@@ -8,7 +8,7 @@ const { resolveKernelDatabasePath } = require('./kernel/cli-broker-factory');
8
8
  const { resolveGitCommonDir } = require('./kernel/broker');
9
9
  const { createBuiltinSQLiteDriver } = require('./kernel/sqlite-driver');
10
10
  const { normalizeRecallHit } = require('./memory-recall');
11
- const { createUsageEvidenceStore } = require('../packages/memory');
11
+ const { createUsageEvidenceStore } = require('@forge/memory');
12
12
 
13
13
  // Project memory is a Forge read model persisted in the kernel store (kernel_memories),
14
14
  // written DIRECTLY rather than through the issue CAS/guarded-event path. The store seam