@fitlab-ai/agent-infra 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +6 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +58 -21
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +173 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +56 -67
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +2 -1
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-orchestration.js +156 -291
- package/dist/lib/platform/pr-summary.js +48 -10
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +137 -5
- package/dist/lib/sandbox/control/lifecycle.js +3 -1
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/delegation-receipts.js +243 -2
- package/dist/lib/task/finalization.js +567 -0
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +187 -761
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +113 -60
- package/dist/lib/task/verification.js +2 -1
- package/dist/lib/task/workflow-warning-intents.js +50 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +83 -91
- package/lib/git/workflow.ts +11 -2
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +2 -1
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-orchestration.ts +20 -103
- package/lib/platform/pr-summary.ts +55 -11
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +163 -5
- package/lib/sandbox/control/lifecycle.ts +2 -1
- package/lib/sandbox/control/protocol.ts +80 -7
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +3 -3
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/delegation-receipts.ts +258 -1
- package/lib/task/finalization.ts +719 -0
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +217 -982
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +118 -63
- package/lib/task/verification.ts +3 -2
- package/lib/task/workflow-warning-intents.ts +57 -2
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
- package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -343,6 +343,64 @@ function loadShortIdByTaskId(repoRoot) {
|
|
|
343
343
|
}
|
|
344
344
|
return map;
|
|
345
345
|
}
|
|
346
|
+
function inspectShortIdRegistry(repoRoot) {
|
|
347
|
+
const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
|
|
348
|
+
if (!fs.existsSync(registryPath)) {
|
|
349
|
+
return {
|
|
350
|
+
status: 'missing',
|
|
351
|
+
error: {
|
|
352
|
+
code: 'SHORT_ID_REGISTRY_NOT_FOUND',
|
|
353
|
+
message: 'canonical short-id registry is missing'
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
let raw;
|
|
358
|
+
try {
|
|
359
|
+
raw = fs.readFileSync(registryPath, 'utf8');
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return {
|
|
363
|
+
status: 'invalid',
|
|
364
|
+
error: {
|
|
365
|
+
code: 'SHORT_ID_REGISTRY_READ_FAILED',
|
|
366
|
+
message: 'canonical short-id registry is unreadable'
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
let parsed;
|
|
371
|
+
try {
|
|
372
|
+
parsed = JSON.parse(raw);
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return {
|
|
376
|
+
status: 'invalid',
|
|
377
|
+
error: {
|
|
378
|
+
code: 'SHORT_ID_REGISTRY_INVALID_JSON',
|
|
379
|
+
message: 'canonical short-id registry contains invalid JSON'
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const registry = validateRegistry(parsed, registryPath, configuredShortIdLength(repoRoot));
|
|
385
|
+
const shortIds = new Map();
|
|
386
|
+
for (const [key, taskId] of Object.entries(registry.ids))
|
|
387
|
+
shortIds.set(taskId, key);
|
|
388
|
+
return { status: 'valid', shortIds };
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
return {
|
|
392
|
+
status: 'invalid',
|
|
393
|
+
error: {
|
|
394
|
+
code: typeof error.code === 'string'
|
|
395
|
+
? error.code
|
|
396
|
+
: 'SHORT_ID_REGISTRY_INVALID_SCHEMA',
|
|
397
|
+
message: error.code === 'SHORT_ID_REGISTRY_DUPLICATE_TASK'
|
|
398
|
+
? 'canonical short-id registry contains duplicate task entries'
|
|
399
|
+
: 'canonical short-id registry has invalid schema'
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
346
404
|
/**
|
|
347
405
|
* Resolve a branch to its active-task short id (`NN`), or `null` when no
|
|
348
406
|
* active task is bound to that branch.
|
|
@@ -371,5 +429,5 @@ function lookupShortIdByBranch(branch, repoRoot, _opts) {
|
|
|
371
429
|
}
|
|
372
430
|
return matches[0];
|
|
373
431
|
}
|
|
374
|
-
export { normalizeShortIdInput, isRemovedHashShortIdInput, resolveShortIdReadOnly, lookupShortIdByBranch, loadShortIdByTaskId, configuredShortIdLength, mutateShortIdRegistry, executeShortIdCommand };
|
|
432
|
+
export { normalizeShortIdInput, isRemovedHashShortIdInput, resolveShortIdReadOnly, lookupShortIdByBranch, loadShortIdByTaskId, inspectShortIdRegistry, configuredShortIdLength, mutateShortIdRegistry, executeShortIdCommand };
|
|
375
433
|
//# sourceMappingURL=short-id.js.map
|
|
@@ -207,6 +207,8 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
|
|
|
207
207
|
catch (error) {
|
|
208
208
|
if (error instanceof TaskExecutionLockError)
|
|
209
209
|
throw error;
|
|
210
|
+
if (error instanceof Error && error.name === 'OrchestrationStateError')
|
|
211
|
+
throw error;
|
|
210
212
|
throw new TaskExecutionLockError('ORCHESTRATION_LOCK_FAILED', 'lifecycle task lock operation failed', { operation: ownsFixed ? 'callback' : 'acquire', errno: errno(error), key });
|
|
211
213
|
}
|
|
212
214
|
finally {
|
|
@@ -228,5 +230,8 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
|
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
}
|
|
231
|
-
|
|
233
|
+
function withRepositoryMutationLock(repoRoot, callback, options = {}) {
|
|
234
|
+
return withTaskExecutionLock(repoRoot, '__repository_mutation__', 'repository-mutation', callback, options);
|
|
235
|
+
}
|
|
236
|
+
export { TaskExecutionLockError, lockKey, mapLinkError, withRepositoryMutationLock, withTaskExecutionLock };
|
|
232
237
|
//# sourceMappingURL=task-execution-lock.js.map
|
|
@@ -16,6 +16,7 @@ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.
|
|
|
16
16
|
import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
|
|
17
17
|
import { loadVerificationConfig } from "./verification-config.js";
|
|
18
18
|
import { snapshotReview } from "../git/review-snapshot.js";
|
|
19
|
+
import { OrchestrationStateError, readRun } from "./orchestration.js";
|
|
19
20
|
const TASK_ENUMS = {
|
|
20
21
|
type: ["feature", "bugfix", "refactor", "docs", "chore"],
|
|
21
22
|
workflow: ["feature-development", "bug-fix", "refactoring"],
|
|
@@ -104,6 +105,8 @@ function runCheck(type, context, shared) {
|
|
|
104
105
|
return checkReviewFact(context);
|
|
105
106
|
case "post-review-commit":
|
|
106
107
|
return checkPostReviewCommit(context);
|
|
108
|
+
case "required-pr-delivery":
|
|
109
|
+
return checkRequiredPrDelivery(context);
|
|
107
110
|
case "orchestration-state":
|
|
108
111
|
return checkOrchestrationState(context);
|
|
109
112
|
case "orchestration-evidence":
|
|
@@ -120,22 +123,6 @@ function runCheck(type, context, shared) {
|
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
|
-
function isFailClosedLegacyPause(run) {
|
|
124
|
-
if (run.schemaVersion !== 1
|
|
125
|
-
|| run.status !== 'paused'
|
|
126
|
-
|| run.pause?.recoverable !== false
|
|
127
|
-
|| !exactText(run.pause?.message)
|
|
128
|
-
|| !Array.isArray(run.receipts)) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
if (run.pause.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED') {
|
|
132
|
-
return run.receipts.length > 0 && run.pendingDelegation === null;
|
|
133
|
-
}
|
|
134
|
-
if (run.pause.code === 'ORCHESTRATION_DELEGATION_BUSY') {
|
|
135
|
-
return run.pendingDelegation != null && run.pendingDelegation.status !== 'sealed';
|
|
136
|
-
}
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
126
|
function checkOrchestrationState({ taskDir }) {
|
|
140
127
|
const file = path.join(taskDir, 'orchestration.json');
|
|
141
128
|
const stat = safeStat(file);
|
|
@@ -143,16 +130,14 @@ function checkOrchestrationState({ taskDir }) {
|
|
|
143
130
|
return failResult('orchestration-state', 'orchestration.json is missing');
|
|
144
131
|
let run;
|
|
145
132
|
try {
|
|
146
|
-
run =
|
|
133
|
+
run = readRun(taskDir);
|
|
147
134
|
}
|
|
148
135
|
catch (error) {
|
|
149
|
-
|
|
136
|
+
const message = error instanceof OrchestrationStateError ? error.message : String(error);
|
|
137
|
+
return failResult('orchestration-state', `Invalid orchestration.json: ${message}`);
|
|
150
138
|
}
|
|
151
|
-
if (
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
if (![2, 3].includes(run.schemaVersion) || !['paused', 'completed'].includes(run.status)) {
|
|
155
|
-
return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
|
|
139
|
+
if (!run || !['paused', 'completed'].includes(run.status)) {
|
|
140
|
+
return failResult('orchestration-state', `Expected paused or completed run, received '${run?.status ?? 'missing'}'`);
|
|
156
141
|
}
|
|
157
142
|
if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
|
|
158
143
|
return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
|
|
@@ -215,13 +200,11 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
215
200
|
return failResult('orchestration-evidence', 'orchestration.json is missing');
|
|
216
201
|
let run;
|
|
217
202
|
try {
|
|
218
|
-
run =
|
|
203
|
+
run = readRun(taskDir);
|
|
219
204
|
}
|
|
220
205
|
catch (error) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (isFailClosedLegacyPause(run)) {
|
|
224
|
-
return passResult('orchestration-evidence', 'Legacy orchestration evidence is preserved in a fail-closed pause');
|
|
206
|
+
const message = error instanceof OrchestrationStateError ? error.message : String(error);
|
|
207
|
+
return failResult('orchestration-evidence', `Invalid orchestration.json: ${message}`);
|
|
225
208
|
}
|
|
226
209
|
const policy = run.modelPolicy;
|
|
227
210
|
if (!policy
|
|
@@ -239,30 +222,8 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
239
222
|
return failResult('orchestration-evidence', 'Run model policy source or recovery history is invalid');
|
|
240
223
|
}
|
|
241
224
|
for (const recovery of run.recoveryHistory) {
|
|
242
|
-
const validModelRecovery = recovery.code === 'MODEL_POLICY_SUPPLEMENTED'
|
|
243
|
-
&& recovery.previousSchemaVersion === 1
|
|
244
|
-
&& recovery.receiptCount === 0
|
|
245
|
-
&& recovery.pendingDelegation === false
|
|
246
|
-
&& exactText(recovery.recoveredAt)
|
|
247
|
-
&& recovery.policySource;
|
|
248
225
|
const guards = recovery.guards;
|
|
249
|
-
const
|
|
250
|
-
&& recovery.previousSchemaVersion === 2
|
|
251
|
-
&& recovery.previousStatus === 'paused'
|
|
252
|
-
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
253
|
-
&& recovery.client === 'codex'
|
|
254
|
-
&& recovery.resultingStatus === 'running'
|
|
255
|
-
&& exactText(recovery.recoveredAt)
|
|
256
|
-
&& guards?.stepCount === 0
|
|
257
|
-
&& guards?.nextStage === null
|
|
258
|
-
&& guards?.baselineEmpty === true
|
|
259
|
-
&& guards?.receiptCount === 0
|
|
260
|
-
&& guards?.pendingDelegation === false
|
|
261
|
-
&& guards?.commitAuthorizationUnused === true
|
|
262
|
-
&& guards?.completionEvidenceAbsent === true
|
|
263
|
-
&& guards?.commitIntentAbsent === true;
|
|
264
|
-
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION'
|
|
265
|
-
&& recovery.previousSchemaVersion === 3
|
|
226
|
+
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED'
|
|
266
227
|
&& recovery.previousStatus === 'paused'
|
|
267
228
|
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
268
229
|
&& recovery.client === 'claude-code'
|
|
@@ -274,9 +235,8 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
274
235
|
&& guards?.receiptCount === 0
|
|
275
236
|
&& guards?.pendingDelegation === false
|
|
276
237
|
&& guards?.commitAuthorizationUnused === true
|
|
277
|
-
&& guards?.completionEvidenceAbsent === true
|
|
278
|
-
|
|
279
|
-
if (!validModelRecovery && !validCapabilityRecovery && !validClaudeCodeRecovery) {
|
|
238
|
+
&& guards?.completionEvidenceAbsent === true;
|
|
239
|
+
if (!validClaudeCodeRecovery) {
|
|
280
240
|
return failResult('orchestration-evidence', 'Run recovery history contains invalid provenance');
|
|
281
241
|
}
|
|
282
242
|
}
|
|
@@ -342,11 +302,34 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
342
302
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
|
|
343
303
|
}
|
|
344
304
|
if (receipt.client === 'codex') {
|
|
305
|
+
const activatedCodex = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
345
306
|
const host = receipt.hostEvidence;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
||
|
|
349
|
-
|
|
307
|
+
const provenance = receipt.lifecycleProvenance;
|
|
308
|
+
if (!provenance
|
|
309
|
+
|| (activatedCodex && (host?.kind !== 'codex-lifecycle-v2'
|
|
310
|
+
|| host.protocolVersion !== provenance.protocolVersion
|
|
311
|
+
|| host.packageVersion !== provenance.packageVersion
|
|
312
|
+
|| host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
|
|
313
|
+
|| host.lifecycleContractHash !== provenance.lifecycleContractHash
|
|
314
|
+
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
315
|
+
|| host.hookSource !== provenance.hookSource
|
|
316
|
+
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
|
317
|
+
|| host.hookSourceHash !== provenance.hookSourceHash
|
|
318
|
+
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
319
|
+
|| receipt.parentId !== provenance.capabilitySessionId
|
|
320
|
+
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
321
|
+
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
322
|
+
|| host.controlGeneration !== provenance.controlGeneration
|
|
323
|
+
|| host.spawnToolUseId === provenance.capabilityToolUseId
|
|
324
|
+
|| !exactText(host.spawnToolUseId)
|
|
325
|
+
|| !Number.isFinite(Date.parse(host.spawnObservedAt ?? ''))
|
|
326
|
+
|| !Number.isFinite(Date.parse(receipt.spawnDispatchedAt ?? ''))
|
|
327
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt ?? ''))
|
|
328
|
+
|| Date.parse(host.spawnObservedAt ?? '') < Date.parse(receipt.spawnDispatchedAt ?? '')
|
|
329
|
+
|| Date.parse(host.spawnObservedAt ?? '') > Date.parse(receipt.activationDeadlineAt ?? '')
|
|
330
|
+
|| !exactText(host.hookDefinitionHash)
|
|
331
|
+
|| !Number.isSafeInteger(host.startRevision)
|
|
332
|
+
|| host.startRevision < 1))) {
|
|
350
333
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid Codex start evidence`);
|
|
351
334
|
}
|
|
352
335
|
if (['sealed', 'consumed'].includes(receipt.status) && (!Number.isSafeInteger(host.stopRevision)
|
|
@@ -438,6 +421,60 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
438
421
|
const warningSuffix = warnings.length > 0 ? `; warnings: ${warnings.join("; ")}` : "";
|
|
439
422
|
return passResult("task-meta", `Task metadata valid (${requiredFields.length} required fields checked${warningSuffix})`, warnings);
|
|
440
423
|
}
|
|
424
|
+
function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
|
|
425
|
+
const task = loadTask(taskDir);
|
|
426
|
+
if (!task.ok)
|
|
427
|
+
return failResult('required-pr-delivery', task.message);
|
|
428
|
+
let projectConfig = {};
|
|
429
|
+
try {
|
|
430
|
+
const file = path.join(repositoryRoot, '.agents', '.airc.json');
|
|
431
|
+
if (safeStat(file)?.isFile())
|
|
432
|
+
projectConfig = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return failResult('required-pr-delivery', 'Project configuration is invalid');
|
|
436
|
+
}
|
|
437
|
+
const flow = projectConfig.prFlow;
|
|
438
|
+
const status = String(task.metadata.pr_status || 'pending');
|
|
439
|
+
const prNumber = parsePrNumber(task.metadata.pr_number);
|
|
440
|
+
if (flow === 'disabled')
|
|
441
|
+
return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
|
|
442
|
+
if (flow === 'required' && (status !== 'created' || prNumber === null)) {
|
|
443
|
+
return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
|
|
444
|
+
}
|
|
445
|
+
if (flow !== 'required' && status === 'pending') {
|
|
446
|
+
return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
|
|
447
|
+
}
|
|
448
|
+
if (status === 'created' && prNumber === null) {
|
|
449
|
+
return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
|
|
450
|
+
}
|
|
451
|
+
if (flow === 'required') {
|
|
452
|
+
if (mode === 'gate') {
|
|
453
|
+
return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
|
|
454
|
+
}
|
|
455
|
+
const inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: repositoryRoot });
|
|
456
|
+
if (inspected.status === 'blocked') {
|
|
457
|
+
return blockedResult('required-pr-delivery', inspected.error?.message || 'Unable to inspect the required pull request', inspected.error?.code || 'PR_INSPECTION_BLOCKED');
|
|
458
|
+
}
|
|
459
|
+
const pullRequest = inspected.pullRequest;
|
|
460
|
+
const repository = inspected.platform.repository?.toLowerCase() || '';
|
|
461
|
+
const taskBranch = String(task.metadata.branch || '').trim();
|
|
462
|
+
if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
|
|
463
|
+
return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
|
|
464
|
+
}
|
|
465
|
+
if (!repository
|
|
466
|
+
|| pullRequest.repository.toLowerCase() !== repository
|
|
467
|
+
|| pullRequest.base.repository.toLowerCase() !== repository
|
|
468
|
+
|| (taskBranch && pullRequest.head.ref !== taskBranch)) {
|
|
469
|
+
return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
|
|
470
|
+
}
|
|
471
|
+
if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
|
|
472
|
+
return failResult('required-pr-delivery', 'Project policy requires the bound pull request to be merged');
|
|
473
|
+
}
|
|
474
|
+
return passResult('required-pr-delivery', `Pull request delivery policy satisfied (merged PR #${prNumber})`);
|
|
475
|
+
}
|
|
476
|
+
return passResult('required-pr-delivery', `Pull request delivery policy satisfied (${status})`);
|
|
477
|
+
}
|
|
441
478
|
function validateTaskBranch(metadata) {
|
|
442
479
|
if (isBlank(metadata.branch)) {
|
|
443
480
|
return null;
|
|
@@ -1337,7 +1374,14 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
|
|
|
1337
1374
|
for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
|
|
1338
1375
|
if (checkConfig === null)
|
|
1339
1376
|
continue;
|
|
1340
|
-
const result = runCheck(type, {
|
|
1377
|
+
const result = runCheck(type, {
|
|
1378
|
+
mode,
|
|
1379
|
+
skillName,
|
|
1380
|
+
taskDir: path.resolve(taskDir),
|
|
1381
|
+
artifactFile,
|
|
1382
|
+
repositoryRoot: repoRoot,
|
|
1383
|
+
config: checkConfig
|
|
1384
|
+
}, shared);
|
|
1341
1385
|
checks.push(result);
|
|
1342
1386
|
if (result.status === "blocked")
|
|
1343
1387
|
break;
|
|
@@ -1351,7 +1395,16 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
|
|
|
1351
1395
|
return { skill: skillName, ...failResult(type, `Unknown check type '${type}' for skill '${skillName}'.`) };
|
|
1352
1396
|
if (config === null)
|
|
1353
1397
|
return { skill: skillName, ...passResult(type, `Check '${type}' is disabled for skill '${skillName}'.`) };
|
|
1354
|
-
return {
|
|
1398
|
+
return {
|
|
1399
|
+
skill: skillName,
|
|
1400
|
+
...runCheck(type, {
|
|
1401
|
+
skillName,
|
|
1402
|
+
taskDir: path.resolve(taskDir),
|
|
1403
|
+
artifactFile,
|
|
1404
|
+
repositoryRoot: repoRoot,
|
|
1405
|
+
config
|
|
1406
|
+
}, shared)
|
|
1407
|
+
};
|
|
1355
1408
|
}
|
|
1356
1409
|
export { verifyInProcess };
|
|
1357
1410
|
//# sourceMappingURL=verification-engine.js.map
|
|
@@ -21,7 +21,8 @@ const VERIFICATION_CATALOG = {
|
|
|
21
21
|
'block-task.completed': gate('block-task', 'blocked'),
|
|
22
22
|
'cancel-task.completed': gate('cancel-task', 'completed'),
|
|
23
23
|
'commit.completed': gate('commit', 'active'),
|
|
24
|
-
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['
|
|
24
|
+
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
25
|
+
'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
25
26
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
26
27
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
27
28
|
'create-task.completed': gate('create-task', 'active'),
|
|
@@ -3,6 +3,7 @@ import { resolveTaskRef } from './resolve-ref.js';
|
|
|
3
3
|
import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
|
|
4
4
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
5
5
|
import { allowsManualOverride } from './guard-override.js';
|
|
6
|
+
import { retryHintForWarning } from './operation-outcome.js';
|
|
6
7
|
function failed(intent, code, message, taskId = null, entityId = null) {
|
|
7
8
|
return { status: 'failed', changed: false, intent: intent.kind, taskId, entityId, before: null, after: null, warnings: [], operations: [], error: { code, message } };
|
|
8
9
|
}
|
|
@@ -129,5 +130,53 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
129
130
|
entityId: after.id, before, after, warnings: [], operations: writeResult.operations, error: null
|
|
130
131
|
};
|
|
131
132
|
}
|
|
132
|
-
|
|
133
|
+
function projectFinalizationWarning(taskRef, warning, options = {}) {
|
|
134
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
135
|
+
if (!resolved.ok)
|
|
136
|
+
return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
|
|
137
|
+
if (resolved.state !== 'completed')
|
|
138
|
+
return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected completed` } };
|
|
139
|
+
let content;
|
|
140
|
+
let rows;
|
|
141
|
+
try {
|
|
142
|
+
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
143
|
+
rows = parseWorkflowWarnings(content);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return { status: 'failed', changed: false, error: { code: 'WARNING_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) } };
|
|
147
|
+
}
|
|
148
|
+
const metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
|
|
149
|
+
const existing = rows.find((row) => row.step === warning.step && row.code === warning.code && row.target === warning.target);
|
|
150
|
+
const after = existing
|
|
151
|
+
? {
|
|
152
|
+
...existing,
|
|
153
|
+
severity: warning.severity,
|
|
154
|
+
status: warning.status,
|
|
155
|
+
message: warning.message,
|
|
156
|
+
action: retryHintForWarning(warning),
|
|
157
|
+
resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
|
|
158
|
+
resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
|
|
159
|
+
}
|
|
160
|
+
: {
|
|
161
|
+
id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
|
|
162
|
+
severity: warning.severity, code: warning.code, status: warning.status,
|
|
163
|
+
target: warning.target, message: warning.message, action: retryHintForWarning(warning),
|
|
164
|
+
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
|
|
165
|
+
};
|
|
166
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(after))
|
|
167
|
+
return { status: 'no-op', changed: false, error: null };
|
|
168
|
+
const written = writeTask({
|
|
169
|
+
taskRef: resolved.taskId,
|
|
170
|
+
expectedState: 'completed',
|
|
171
|
+
mutations: [...sectionMutation(content), rowMutation(after)]
|
|
172
|
+
}, {
|
|
173
|
+
...options,
|
|
174
|
+
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: 'completed' },
|
|
175
|
+
metadataProvider: () => metadata
|
|
176
|
+
});
|
|
177
|
+
if (written.status === 'failed')
|
|
178
|
+
return { status: 'failed', changed: false, error: { code: written.error.code, message: written.error.message } };
|
|
179
|
+
return { status: written.status === 'applied' ? 'applied' : 'no-op', changed: written.changed, error: null };
|
|
180
|
+
}
|
|
181
|
+
export { applyWorkflowWarningIntent, projectFinalizationWarning };
|
|
133
182
|
//# sourceMappingURL=workflow-warning-intents.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.9.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.9.9",
|
|
4
|
+
"sourceInputHash": "2f2db695db557531b3139eccd1bd530fbeb7b99409e2d49338f3b4646aac9ef6",
|
|
5
|
+
"internalExecutableBuildHash": "1391ebe2614f3071e7a60920325046a47f1473e7fb23c821327072f947f639d3"
|
|
6
6
|
}
|
|
@@ -36,6 +36,10 @@ type AppServerTransportOptions = Readonly<{
|
|
|
36
36
|
timeoutMs?: number;
|
|
37
37
|
rolloutReadAttempts?: number;
|
|
38
38
|
rolloutRetryMs?: number;
|
|
39
|
+
threadReadAttempts?: number;
|
|
40
|
+
threadRetryMs?: number;
|
|
41
|
+
terminalReadAttempts?: number;
|
|
42
|
+
terminalRetryMs?: number;
|
|
39
43
|
}>;
|
|
40
44
|
|
|
41
45
|
type CodexRuntimeIdentity = Readonly<{
|
|
@@ -52,6 +56,10 @@ const LIFECYCLE_HOOKS = Object.freeze([
|
|
|
52
56
|
]);
|
|
53
57
|
const DEFAULT_ROLLOUT_READ_ATTEMPTS = 81;
|
|
54
58
|
const DEFAULT_ROLLOUT_RETRY_MS = 100;
|
|
59
|
+
const DEFAULT_THREAD_READ_ATTEMPTS = 21;
|
|
60
|
+
const DEFAULT_THREAD_RETRY_MS = 100;
|
|
61
|
+
const DEFAULT_TERMINAL_READ_ATTEMPTS = 21;
|
|
62
|
+
const DEFAULT_TERMINAL_RETRY_MS = 100;
|
|
55
63
|
|
|
56
64
|
function object(value: unknown): JsonObject | null {
|
|
57
65
|
return value && typeof value === 'object' && !Array.isArray(value)
|
|
@@ -67,6 +75,7 @@ function resolveCodexSpawnedChild(
|
|
|
67
75
|
transcriptPath: string,
|
|
68
76
|
expected: Readonly<{
|
|
69
77
|
sessionId: string;
|
|
78
|
+
turnId: string;
|
|
70
79
|
toolUseId: string;
|
|
71
80
|
nativeAgent: string;
|
|
72
81
|
taskName: string;
|
|
@@ -111,15 +120,19 @@ function resolveCodexSpawnedChild(
|
|
|
111
120
|
) throw new Error('Codex parent rollout spawn identity does not match the hook event');
|
|
112
121
|
const activities = records.filter((record) => {
|
|
113
122
|
const payload = object(record?.payload);
|
|
123
|
+
const item = object(payload?.item);
|
|
114
124
|
return record?.type === 'event_msg'
|
|
115
|
-
&& payload?.type === '
|
|
116
|
-
&& payload.
|
|
117
|
-
&& payload.
|
|
118
|
-
&&
|
|
119
|
-
&&
|
|
125
|
+
&& payload?.type === 'item_completed'
|
|
126
|
+
&& payload.thread_id === expected.sessionId
|
|
127
|
+
&& payload.turn_id === expected.turnId
|
|
128
|
+
&& item?.type === 'SubAgentActivity'
|
|
129
|
+
&& item.id === expected.toolUseId
|
|
130
|
+
&& item.kind === 'started'
|
|
131
|
+
&& item.agent_path === `/root/${expected.taskName}`
|
|
132
|
+
&& nonEmpty(item.agent_thread_id);
|
|
120
133
|
});
|
|
121
134
|
if (activities.length !== 1) throw new Error('Codex parent rollout child activity was not found uniquely');
|
|
122
|
-
return nonEmpty(object(activities[0]?.payload)?.agent_thread_id)!;
|
|
135
|
+
return nonEmpty(object(object(activities[0]?.payload)?.item)?.agent_thread_id)!;
|
|
123
136
|
}
|
|
124
137
|
|
|
125
138
|
function validateCodexLifecycleHookConfig(value: unknown): void {
|
|
@@ -133,7 +146,7 @@ function validateCodexLifecycleHookConfig(value: unknown): void {
|
|
|
133
146
|
return entry.hooks.some((hookValue) => {
|
|
134
147
|
const hook = object(hookValue);
|
|
135
148
|
return hook?.type === 'command'
|
|
136
|
-
&& hook.timeout ===
|
|
149
|
+
&& hook.timeout === 30
|
|
137
150
|
&& hook.command === `node "$(git rev-parse --show-toplevel)/.agents/hooks/lifecycle-delegation.js" --client codex --event ${phase}`;
|
|
138
151
|
});
|
|
139
152
|
});
|
|
@@ -478,18 +491,30 @@ async function resolveCodexThread(
|
|
|
478
491
|
const readResult = object(await transport.request('thread/read', { threadId: childThreadId, includeTurns: false }));
|
|
479
492
|
const readThread = object(readResult?.thread);
|
|
480
493
|
if (nonEmpty(readThread?.id) !== childThreadId) throw new Error('Codex App Server returned the wrong child thread');
|
|
481
|
-
let
|
|
494
|
+
let rolloutRecords: readonly unknown[];
|
|
482
495
|
try {
|
|
483
|
-
|
|
496
|
+
rolloutRecords = await resolveCodexRolloutRecords(
|
|
484
497
|
readResult,
|
|
485
498
|
childThreadId,
|
|
486
499
|
options.rolloutReadAttempts ?? DEFAULT_ROLLOUT_READ_ATTEMPTS,
|
|
487
500
|
options.rolloutRetryMs ?? DEFAULT_ROLLOUT_RETRY_MS
|
|
488
501
|
);
|
|
489
|
-
resolution = parseCodexThreadResolution(readResult, rolloutRecords);
|
|
490
502
|
} catch {
|
|
491
503
|
throw new Error('Codex App Server rollout metadata is unavailable');
|
|
492
504
|
}
|
|
505
|
+
let resolution = parseCodexThreadResolution(readResult, rolloutRecords);
|
|
506
|
+
const threadReadAttempts = options.threadReadAttempts ?? DEFAULT_THREAD_READ_ATTEMPTS;
|
|
507
|
+
for (let attempt = 1; resolution.thread.forkedFromId !== null && attempt < threadReadAttempts; attempt += 1) {
|
|
508
|
+
await new Promise((resolve) => setTimeout(resolve, options.threadRetryMs ?? DEFAULT_THREAD_RETRY_MS));
|
|
509
|
+
const refreshed = object(await transport.request('thread/read', { threadId: childThreadId, includeTurns: false }));
|
|
510
|
+
if (nonEmpty(object(refreshed?.thread)?.id) !== childThreadId) {
|
|
511
|
+
throw new Error('Codex App Server returned the wrong child thread');
|
|
512
|
+
}
|
|
513
|
+
resolution = parseCodexThreadResolution(refreshed, rolloutRecords);
|
|
514
|
+
}
|
|
515
|
+
if (resolution.thread.forkedFromId !== null) {
|
|
516
|
+
throw new Error('Codex App Server fresh thread metadata is unavailable');
|
|
517
|
+
}
|
|
493
518
|
const reroutes = transport.notifications
|
|
494
519
|
.filter((entry) => entry.method === 'model/rerouted')
|
|
495
520
|
.map((entry) => parseCodexModelReroute(entry.params))
|
|
@@ -507,20 +532,37 @@ async function resolveCodexThread(
|
|
|
507
532
|
|
|
508
533
|
async function resolveCodexTerminal(
|
|
509
534
|
childThreadId: string,
|
|
510
|
-
|
|
535
|
+
expectedTurnIdOrOptions: string | AppServerTransportOptions = {},
|
|
536
|
+
terminalOptions: AppServerTransportOptions = {}
|
|
511
537
|
): Promise<Extract<CodexLifecycleEvent, { type: 'app-terminal' }>> {
|
|
538
|
+
const expectedTurnId = typeof expectedTurnIdOrOptions === 'string' ? expectedTurnIdOrOptions : null;
|
|
539
|
+
const options = typeof expectedTurnIdOrOptions === 'string' ? terminalOptions : expectedTurnIdOrOptions;
|
|
512
540
|
const transport = new CodexAppServerTransport(options);
|
|
513
541
|
transport.start();
|
|
514
542
|
try {
|
|
515
543
|
await transport.initialize();
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
544
|
+
const attempts = expectedTurnId ? options.terminalReadAttempts ?? DEFAULT_TERMINAL_READ_ATTEMPTS : 1;
|
|
545
|
+
let abnormal: Extract<CodexLifecycleEvent, { type: 'app-terminal' }> | null = null;
|
|
546
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
547
|
+
const result = object(await transport.request('thread/read', { threadId: childThreadId, includeTurns: true }));
|
|
548
|
+
const thread = object(result?.thread);
|
|
549
|
+
if (nonEmpty(thread?.id) !== childThreadId) throw new Error('Codex App Server returned the wrong child thread');
|
|
550
|
+
const turns = Array.isArray(thread?.turns) ? thread.turns : [];
|
|
551
|
+
const turn = expectedTurnId
|
|
552
|
+
? turns.find((candidate) => object(candidate)?.id === expectedTurnId)
|
|
553
|
+
: turns.at(-1);
|
|
554
|
+
const turnValue = object(turn);
|
|
555
|
+
if (turnValue && turnValue.status !== 'inProgress') {
|
|
556
|
+
const terminal = parseCodexTurnCompleted({ threadId: childThreadId, turn });
|
|
557
|
+
if (terminal.status === 'completed') return terminal;
|
|
558
|
+
abnormal = terminal;
|
|
559
|
+
}
|
|
560
|
+
if (attempt + 1 < attempts) {
|
|
561
|
+
await new Promise((resolve) => setTimeout(resolve, options.terminalRetryMs ?? DEFAULT_TERMINAL_RETRY_MS));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (abnormal) return abnormal;
|
|
565
|
+
throw new Error('CODEX_TURN_NOT_TERMINAL');
|
|
524
566
|
} finally {
|
|
525
567
|
transport.close();
|
|
526
568
|
}
|
|
@@ -3,6 +3,8 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
|
|
6
|
+
import semver from 'semver';
|
|
7
|
+
|
|
6
8
|
const LIFECYCLE_PROTOCOL_VERSION = 3;
|
|
7
9
|
|
|
8
10
|
type LifecycleBuildIdentity = Readonly<{
|
|
@@ -32,6 +34,14 @@ function exactText(value: unknown): value is string {
|
|
|
32
34
|
return typeof value === 'string' && value.trim() === value && value.length > 0;
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
function isCanonicalLifecyclePackageVersion(value: unknown): value is string {
|
|
38
|
+
return typeof value === 'string' && semver.valid(value) === value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isLifecycleProtocolVersion(value: unknown): value is typeof LIFECYCLE_PROTOCOL_VERSION {
|
|
42
|
+
return value === LIFECYCLE_PROTOCOL_VERSION;
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
function normalizeRelativePath(value: string): string {
|
|
36
46
|
return value.replaceAll('\\', '/');
|
|
37
47
|
}
|
|
@@ -215,6 +225,8 @@ function verifyLifecycleBuildIdentity(
|
|
|
215
225
|
export {
|
|
216
226
|
LIFECYCLE_PROTOCOL_VERSION,
|
|
217
227
|
computeLifecycleBuildIdentity,
|
|
228
|
+
isCanonicalLifecyclePackageVersion,
|
|
229
|
+
isLifecycleProtocolVersion,
|
|
218
230
|
readLifecycleManifestFiles,
|
|
219
231
|
resolveLifecycleExecutableFiles,
|
|
220
232
|
verifyLifecycleBuildIdentity
|