@gajae-code/coding-agent 0.18.7 → 0.18.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.
- package/CHANGELOG.md +279 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +2 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +6 -1
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +31 -7
- package/dist/types/session/session-storage.d.ts +62 -2
- package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +17 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +2 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +28 -8
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +159 -20
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -9
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/internal-urls/docs-index.generated.ts +18 -17
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +88 -4
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +75 -6
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +711 -66
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +238 -43
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2647 -294
- package/src/sdk/host/session-runtime.ts +426 -109
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +75 -12
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +67 -38
- package/src/session/agent-session.ts +623 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +376 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +19 -3
- package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +417 -14
- package/src/session/task-artifact-owner-codec.ts +71 -8
- package/src/session/task-artifact-owner-retirement.ts +21 -19
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import type * as nodeFs from "node:fs";
|
|
2
3
|
import { constants as fsConstants, type Stats } from "node:fs";
|
|
3
4
|
import type { FileHandle } from "node:fs/promises";
|
|
4
5
|
import * as fs from "node:fs/promises";
|
|
@@ -89,6 +90,7 @@ export interface WorkflowTransactionJournal {
|
|
|
89
90
|
callee?: CanonicalGjcWorkflowSkill;
|
|
90
91
|
paths: string[];
|
|
91
92
|
steps: string[];
|
|
93
|
+
approval_audit_offset?: number;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export type StateWritePolicy = "source" | "cache";
|
|
@@ -1368,9 +1370,16 @@ export async function removeActiveEntry(
|
|
|
1368
1370
|
);
|
|
1369
1371
|
}
|
|
1370
1372
|
|
|
1371
|
-
|
|
1373
|
+
/**
|
|
1374
|
+
* Read every per-skill active entry. Unreadable JSON propagates; a parsed record
|
|
1375
|
+
* that is not a skill entry (not an object, or without a `skill`) is skipped, or
|
|
1376
|
+
* rejected when `strict` is set so status-bearing readers can tell an existing
|
|
1377
|
+
* but unusable authoritative entry apart from a deleted one.
|
|
1378
|
+
*/
|
|
1379
|
+
async function readActiveEntriesFromDir(
|
|
1372
1380
|
cwd: string,
|
|
1373
|
-
sessionScope
|
|
1381
|
+
sessionScope: string | ActiveSessionScope | undefined,
|
|
1382
|
+
strict: boolean,
|
|
1374
1383
|
): Promise<SkillActiveEntry[]> {
|
|
1375
1384
|
const dir = activeStateDir(path.resolve(cwd), sessionScope);
|
|
1376
1385
|
let names: string[];
|
|
@@ -1383,15 +1392,38 @@ export async function readActiveEntries(
|
|
|
1383
1392
|
const entries: SkillActiveEntry[] = [];
|
|
1384
1393
|
for (const name of names.sort()) {
|
|
1385
1394
|
if (!name.endsWith(".json")) continue;
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1395
|
+
const filePath = path.join(dir, name);
|
|
1396
|
+
const raw = await readJsonIfPresent(filePath);
|
|
1397
|
+
if (raw === undefined) continue;
|
|
1398
|
+
const skill = isPlainObject(raw) ? safeString(raw.skill).trim() : "";
|
|
1399
|
+
if (!skill) {
|
|
1400
|
+
if (strict) throw new Error(`active entry at ${filePath} is not a skill entry record`);
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1390
1403
|
entries.push(raw as SkillActiveEntry);
|
|
1391
1404
|
}
|
|
1392
1405
|
return entries;
|
|
1393
1406
|
}
|
|
1394
1407
|
|
|
1408
|
+
export async function readActiveEntries(
|
|
1409
|
+
cwd: string,
|
|
1410
|
+
sessionScope?: string | ActiveSessionScope,
|
|
1411
|
+
): Promise<SkillActiveEntry[]> {
|
|
1412
|
+
return readActiveEntriesFromDir(cwd, sessionScope, false);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* Failure-observable counterpart of `readActiveEntries`: an entry file that exists
|
|
1417
|
+
* but holds no usable skill entry throws instead of being skipped, so its absence
|
|
1418
|
+
* from the result always means the entry does not exist.
|
|
1419
|
+
*/
|
|
1420
|
+
export async function readActiveEntriesStrict(
|
|
1421
|
+
cwd: string,
|
|
1422
|
+
sessionScope?: string | ActiveSessionScope,
|
|
1423
|
+
): Promise<SkillActiveEntry[]> {
|
|
1424
|
+
return readActiveEntriesFromDir(cwd, sessionScope, true);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1395
1427
|
export async function rebuildActiveSnapshot(
|
|
1396
1428
|
cwd: string,
|
|
1397
1429
|
sessionScope?: string | ActiveSessionScope,
|
|
@@ -1559,6 +1591,7 @@ export async function appendAuditEntry(
|
|
|
1559
1591
|
cwd: string,
|
|
1560
1592
|
sessionIdOrEntry: string | AuditEntry,
|
|
1561
1593
|
maybeEntry?: AuditEntry,
|
|
1594
|
+
options: { lockHeld?: boolean; beforeAppend?: (offset: number) => Promise<unknown> } = {},
|
|
1562
1595
|
): Promise<string> {
|
|
1563
1596
|
const sessionId =
|
|
1564
1597
|
typeof sessionIdOrEntry === "string"
|
|
@@ -1568,8 +1601,50 @@ export async function appendAuditEntry(
|
|
|
1568
1601
|
const entry = typeof sessionIdOrEntry === "string" ? maybeEntry : sessionIdOrEntry;
|
|
1569
1602
|
if (!entry) throw new Error("audit entry is required");
|
|
1570
1603
|
const filePath = resolveGjcTarget(layoutAuditPath(cwd, sessionId), cwd);
|
|
1571
|
-
|
|
1572
|
-
|
|
1604
|
+
const append = async () => {
|
|
1605
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
1606
|
+
let initialStat: nodeFs.BigIntStats | undefined;
|
|
1607
|
+
try {
|
|
1608
|
+
initialStat = await fs.lstat(filePath, { bigint: true });
|
|
1609
|
+
if (initialStat.isSymbolicLink() || !initialStat.isFile()) throw new Error("audit path is not a regular file");
|
|
1610
|
+
} catch (error) {
|
|
1611
|
+
if (!isErrno(error, "ENOENT")) throw error;
|
|
1612
|
+
}
|
|
1613
|
+
const flags = initialStat
|
|
1614
|
+
? fsConstants.O_WRONLY |
|
|
1615
|
+
fsConstants.O_APPEND |
|
|
1616
|
+
(process.platform === "win32" ? 0 : (fsConstants.O_NOFOLLOW ?? 0))
|
|
1617
|
+
: fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_CREAT | fsConstants.O_EXCL;
|
|
1618
|
+
let handle: fs.FileHandle | undefined;
|
|
1619
|
+
try {
|
|
1620
|
+
handle = await fs.open(filePath, flags, 0o600);
|
|
1621
|
+
const openedStat = await handle.stat({ bigint: true });
|
|
1622
|
+
const pathStat = await fs.lstat(filePath, { bigint: true });
|
|
1623
|
+
const sameObject = (left: nodeFs.BigIntStats, right: nodeFs.BigIntStats) =>
|
|
1624
|
+
left.dev === right.dev && left.ino === right.ino && left.nlink === right.nlink;
|
|
1625
|
+
if (
|
|
1626
|
+
openedStat.isSymbolicLink() ||
|
|
1627
|
+
!openedStat.isFile() ||
|
|
1628
|
+
pathStat.isSymbolicLink() ||
|
|
1629
|
+
!pathStat.isFile() ||
|
|
1630
|
+
(initialStat !== undefined && !sameObject(initialStat, openedStat)) ||
|
|
1631
|
+
!sameObject(openedStat, pathStat)
|
|
1632
|
+
)
|
|
1633
|
+
throw new Error("audit path identity changed before append");
|
|
1634
|
+
if (openedStat.size > BigInt(Number.MAX_SAFE_INTEGER))
|
|
1635
|
+
throw new Error("audit path is too large to append safely");
|
|
1636
|
+
await options.beforeAppend?.(Number(openedStat.size));
|
|
1637
|
+
await handle.writeFile(`${JSON.stringify(entry)}\n`, "utf-8");
|
|
1638
|
+
await handle.sync();
|
|
1639
|
+
const afterPathStat = await fs.lstat(filePath, { bigint: true });
|
|
1640
|
+
if (afterPathStat.isSymbolicLink() || !sameObject(openedStat, afterPathStat))
|
|
1641
|
+
throw new Error("audit path identity changed during append");
|
|
1642
|
+
} finally {
|
|
1643
|
+
await handle?.close();
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
if (options.lockHeld) await append();
|
|
1647
|
+
else await withWorkflowStateLock(filePath, append, { cwd });
|
|
1573
1648
|
return filePath;
|
|
1574
1649
|
}
|
|
1575
1650
|
|
|
@@ -1275,13 +1275,25 @@ export async function createUltragoalPlan(input: {
|
|
|
1275
1275
|
return plan;
|
|
1276
1276
|
}
|
|
1277
1277
|
|
|
1278
|
-
|
|
1278
|
+
/**
|
|
1279
|
+
* Scheduler order shared with read-only projections: active, then pending, then failed
|
|
1280
|
+
* only when the caller explicitly opts into retrying failed goals. Blocked,
|
|
1281
|
+
* review_blocked, complete, and superseded goals are never schedulable.
|
|
1282
|
+
*/
|
|
1283
|
+
export function selectNextSchedulableGoal<T extends { status: UltragoalGoalStatus }>(
|
|
1284
|
+
goals: readonly T[],
|
|
1285
|
+
retryFailed: boolean,
|
|
1286
|
+
): T | undefined {
|
|
1279
1287
|
return (
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
(retryFailed ?
|
|
1288
|
+
goals.find(goal => goal.status === "active") ??
|
|
1289
|
+
goals.find(goal => goal.status === "pending") ??
|
|
1290
|
+
(retryFailed ? goals.find(goal => goal.status === "failed") : undefined)
|
|
1283
1291
|
);
|
|
1284
1292
|
}
|
|
1293
|
+
|
|
1294
|
+
function chooseNextGoal(plan: UltragoalPlan, retryFailed: boolean): UltragoalGoal | undefined {
|
|
1295
|
+
return selectNextSchedulableGoal(plan.goals, retryFailed);
|
|
1296
|
+
}
|
|
1285
1297
|
export interface UltragoalRunCompletionState {
|
|
1286
1298
|
requiredGoals: UltragoalGoal[];
|
|
1287
1299
|
incompleteGoals: UltragoalGoal[];
|
|
@@ -174,16 +174,7 @@
|
|
|
174
174
|
"clear",
|
|
175
175
|
"contract",
|
|
176
176
|
"handoff",
|
|
177
|
-
"doctor"
|
|
178
|
-
"kickoff",
|
|
179
|
-
"write-spec",
|
|
180
|
-
"write-artifact",
|
|
181
|
-
"stage",
|
|
182
|
-
"check",
|
|
183
|
-
"apply",
|
|
184
|
-
"discard",
|
|
185
|
-
"read",
|
|
186
|
-
"write"
|
|
177
|
+
"doctor"
|
|
187
178
|
],
|
|
188
179
|
"name": "session-id",
|
|
189
180
|
"type": "string"
|
|
@@ -414,6 +405,11 @@
|
|
|
414
405
|
"to": "handoff",
|
|
415
406
|
"verb": "write-spec"
|
|
416
407
|
},
|
|
408
|
+
{
|
|
409
|
+
"from": "interviewing",
|
|
410
|
+
"to": "handoff",
|
|
411
|
+
"verb": "crystallize"
|
|
412
|
+
},
|
|
417
413
|
{
|
|
418
414
|
"from": "handoff",
|
|
419
415
|
"to": "complete",
|
|
@@ -426,14 +422,6 @@
|
|
|
426
422
|
}
|
|
427
423
|
],
|
|
428
424
|
"typedArgs": [
|
|
429
|
-
{
|
|
430
|
-
"appliesToVerbs": [
|
|
431
|
-
"write",
|
|
432
|
-
"api"
|
|
433
|
-
],
|
|
434
|
-
"name": "input",
|
|
435
|
-
"type": "string"
|
|
436
|
-
},
|
|
437
425
|
{
|
|
438
426
|
"appliesToVerbs": [
|
|
439
427
|
"read",
|
|
@@ -462,13 +450,10 @@
|
|
|
462
450
|
"doctor",
|
|
463
451
|
"kickoff",
|
|
464
452
|
"write-spec",
|
|
465
|
-
"write-artifact",
|
|
466
453
|
"stage",
|
|
467
454
|
"check",
|
|
468
455
|
"apply",
|
|
469
|
-
"discard"
|
|
470
|
-
"read",
|
|
471
|
-
"write"
|
|
456
|
+
"discard"
|
|
472
457
|
],
|
|
473
458
|
"name": "session-id",
|
|
474
459
|
"type": "string"
|
|
@@ -556,6 +541,13 @@
|
|
|
556
541
|
"name": "deep",
|
|
557
542
|
"type": "boolean"
|
|
558
543
|
},
|
|
544
|
+
{
|
|
545
|
+
"appliesToVerbs": [
|
|
546
|
+
"kickoff"
|
|
547
|
+
],
|
|
548
|
+
"name": "trace",
|
|
549
|
+
"type": "boolean"
|
|
550
|
+
},
|
|
559
551
|
{
|
|
560
552
|
"appliesToVerbs": [
|
|
561
553
|
"kickoff"
|
|
@@ -587,6 +579,14 @@
|
|
|
587
579
|
"name": "slug",
|
|
588
580
|
"type": "string"
|
|
589
581
|
},
|
|
582
|
+
{
|
|
583
|
+
"appliesToVerbs": [
|
|
584
|
+
"crystallize"
|
|
585
|
+
],
|
|
586
|
+
"name": "slug",
|
|
587
|
+
"required": true,
|
|
588
|
+
"type": "string"
|
|
589
|
+
},
|
|
590
590
|
{
|
|
591
591
|
"appliesToVerbs": [
|
|
592
592
|
"write-spec"
|
|
@@ -614,7 +614,16 @@
|
|
|
614
614
|
},
|
|
615
615
|
{
|
|
616
616
|
"appliesToVerbs": [
|
|
617
|
+
"write-spec"
|
|
618
|
+
],
|
|
619
|
+
"name": "force",
|
|
620
|
+
"type": "boolean"
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
"appliesToVerbs": [
|
|
624
|
+
"kickoff",
|
|
617
625
|
"write-spec",
|
|
626
|
+
"crystallize",
|
|
618
627
|
"stage",
|
|
619
628
|
"check",
|
|
620
629
|
"apply",
|
|
@@ -622,20 +631,47 @@
|
|
|
622
631
|
"read",
|
|
623
632
|
"write",
|
|
624
633
|
"clear",
|
|
625
|
-
"handoff"
|
|
634
|
+
"handoff",
|
|
635
|
+
"approve-execution"
|
|
626
636
|
],
|
|
627
637
|
"name": "json",
|
|
628
638
|
"type": "boolean"
|
|
629
639
|
},
|
|
630
640
|
{
|
|
631
641
|
"appliesToVerbs": [
|
|
642
|
+
"api"
|
|
643
|
+
],
|
|
644
|
+
"name": "input",
|
|
645
|
+
"type": "string"
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
"appliesToVerbs": [
|
|
649
|
+
"write",
|
|
632
650
|
"stage",
|
|
633
|
-
"
|
|
651
|
+
"crystallize"
|
|
634
652
|
],
|
|
635
653
|
"name": "input",
|
|
636
654
|
"required": true,
|
|
637
655
|
"type": "string"
|
|
638
656
|
},
|
|
657
|
+
{
|
|
658
|
+
"appliesToVerbs": [
|
|
659
|
+
"approve-execution"
|
|
660
|
+
],
|
|
661
|
+
"enumValues": [
|
|
662
|
+
"deep-interview"
|
|
663
|
+
],
|
|
664
|
+
"name": "mode",
|
|
665
|
+
"type": "enum"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
"appliesToVerbs": [
|
|
669
|
+
"crystallize",
|
|
670
|
+
"approve-execution"
|
|
671
|
+
],
|
|
672
|
+
"name": "session-id",
|
|
673
|
+
"type": "string"
|
|
674
|
+
},
|
|
639
675
|
{
|
|
640
676
|
"appliesToVerbs": [
|
|
641
677
|
"write"
|
|
@@ -701,6 +737,10 @@
|
|
|
701
737
|
"name": "write-spec",
|
|
702
738
|
"surface": "command-flag"
|
|
703
739
|
},
|
|
740
|
+
{
|
|
741
|
+
"name": "crystallize",
|
|
742
|
+
"surface": "command-flag"
|
|
743
|
+
},
|
|
704
744
|
{
|
|
705
745
|
"name": "stage",
|
|
706
746
|
"surface": "command-positional"
|
|
@@ -733,6 +773,10 @@
|
|
|
733
773
|
"name": "handoff",
|
|
734
774
|
"surface": "command-positional"
|
|
735
775
|
},
|
|
776
|
+
{
|
|
777
|
+
"name": "approve-execution",
|
|
778
|
+
"surface": "command-positional"
|
|
779
|
+
},
|
|
736
780
|
{
|
|
737
781
|
"name": "graph",
|
|
738
782
|
"planned": true,
|
|
@@ -1017,14 +1061,7 @@
|
|
|
1017
1061
|
"handoff",
|
|
1018
1062
|
"doctor",
|
|
1019
1063
|
"kickoff",
|
|
1020
|
-
"write-
|
|
1021
|
-
"write-artifact",
|
|
1022
|
-
"stage",
|
|
1023
|
-
"check",
|
|
1024
|
-
"apply",
|
|
1025
|
-
"discard",
|
|
1026
|
-
"read",
|
|
1027
|
-
"write"
|
|
1064
|
+
"write-artifact"
|
|
1028
1065
|
],
|
|
1029
1066
|
"name": "session-id",
|
|
1030
1067
|
"type": "string"
|
|
@@ -1403,16 +1440,7 @@
|
|
|
1403
1440
|
"clear",
|
|
1404
1441
|
"contract",
|
|
1405
1442
|
"handoff",
|
|
1406
|
-
"doctor"
|
|
1407
|
-
"kickoff",
|
|
1408
|
-
"write-spec",
|
|
1409
|
-
"write-artifact",
|
|
1410
|
-
"stage",
|
|
1411
|
-
"check",
|
|
1412
|
-
"apply",
|
|
1413
|
-
"discard",
|
|
1414
|
-
"read",
|
|
1415
|
-
"write"
|
|
1443
|
+
"doctor"
|
|
1416
1444
|
],
|
|
1417
1445
|
"name": "session-id",
|
|
1418
1446
|
"type": "string"
|
|
@@ -70,22 +70,16 @@ const PLANNED_ADMIN_VERBS = ["graph", "prune", "migrate", "force-overwrite"] as
|
|
|
70
70
|
|
|
71
71
|
const COMMON_TYPED_ARGS: TypedArgSpec[] = [
|
|
72
72
|
{ name: "input", type: "string", appliesToVerbs: ["write", "api"] },
|
|
73
|
-
{
|
|
73
|
+
{
|
|
74
|
+
name: "mode",
|
|
75
|
+
type: "enum",
|
|
76
|
+
enumValues: [...CANONICAL_GJC_WORKFLOW_SKILLS],
|
|
77
|
+
appliesToVerbs: [...STATE_VERBS],
|
|
78
|
+
},
|
|
74
79
|
{
|
|
75
80
|
name: "session-id",
|
|
76
81
|
type: "string",
|
|
77
|
-
appliesToVerbs: [
|
|
78
|
-
...STATE_VERBS,
|
|
79
|
-
"kickoff",
|
|
80
|
-
"write-spec",
|
|
81
|
-
"write-artifact",
|
|
82
|
-
"stage",
|
|
83
|
-
"check",
|
|
84
|
-
"apply",
|
|
85
|
-
"discard",
|
|
86
|
-
"read",
|
|
87
|
-
"write",
|
|
88
|
-
],
|
|
82
|
+
appliesToVerbs: [...STATE_VERBS, "kickoff", "write-spec", "write-artifact", "stage", "check", "apply", "discard"],
|
|
89
83
|
},
|
|
90
84
|
{ name: "thread-id", type: "string", appliesToVerbs: ["write", "clear", "handoff"] },
|
|
91
85
|
{ name: "turn-id", type: "string", appliesToVerbs: ["write", "clear", "handoff"] },
|
|
@@ -137,9 +131,18 @@ function manifest(input: {
|
|
|
137
131
|
phaseLock?: readonly string[];
|
|
138
132
|
canonicalOverrides?: readonly string[];
|
|
139
133
|
initialState?: string;
|
|
134
|
+
commonTypedArgs?: TypedArgSpec[];
|
|
140
135
|
}): SkillManifest {
|
|
141
136
|
const staleInitialState = initialPhaseForSkill(input.skill);
|
|
142
137
|
const initialState = input.initialState ?? staleInitialState;
|
|
138
|
+
const supportedVerbs = new Set([...input.verbs.map(item => item.name), "api"]);
|
|
139
|
+
const typedArgs = [...(input.commonTypedArgs ?? COMMON_TYPED_ARGS), ...(input.typedArgs ?? [])]
|
|
140
|
+
.map(argument =>
|
|
141
|
+
argument.appliesToVerbs === undefined
|
|
142
|
+
? argument
|
|
143
|
+
: { ...argument, appliesToVerbs: argument.appliesToVerbs.filter(verb => supportedVerbs.has(verb)) },
|
|
144
|
+
)
|
|
145
|
+
.filter(argument => argument.appliesToVerbs === undefined || argument.appliesToVerbs.length > 0);
|
|
143
146
|
return {
|
|
144
147
|
skill: input.skill,
|
|
145
148
|
states: input.states.map(item => state(item, initialState, input.terminalStates)),
|
|
@@ -147,7 +150,7 @@ function manifest(input: {
|
|
|
147
150
|
terminalStates: input.terminalStates,
|
|
148
151
|
transitions: input.transitions,
|
|
149
152
|
verbs: input.verbs,
|
|
150
|
-
typedArgs
|
|
153
|
+
typedArgs,
|
|
151
154
|
retention: input.retention,
|
|
152
155
|
hudFields: input.hudFields,
|
|
153
156
|
graphLabel: input.graphLabel,
|
|
@@ -171,32 +174,63 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
|
|
|
171
174
|
terminalStates: ["handoff", "complete"],
|
|
172
175
|
transitions: [
|
|
173
176
|
{ from: "interviewing", to: "handoff", verb: "write-spec" },
|
|
177
|
+
{ from: "interviewing", to: "handoff", verb: "crystallize" },
|
|
174
178
|
{ from: "handoff", to: "complete", verb: "clear" },
|
|
175
179
|
{ from: "interviewing", to: "complete", verb: "clear" },
|
|
176
180
|
],
|
|
177
181
|
verbs: [
|
|
178
182
|
...stateVerbs(),
|
|
179
|
-
...flagVerbs(["kickoff", "write-spec"]),
|
|
180
|
-
...positionalVerbs([
|
|
183
|
+
...flagVerbs(["kickoff", "write-spec", "crystallize"]),
|
|
184
|
+
...positionalVerbs([
|
|
185
|
+
"stage",
|
|
186
|
+
"check",
|
|
187
|
+
"apply",
|
|
188
|
+
"discard",
|
|
189
|
+
"read",
|
|
190
|
+
"write",
|
|
191
|
+
"clear",
|
|
192
|
+
"handoff",
|
|
193
|
+
"approve-execution",
|
|
194
|
+
]),
|
|
181
195
|
...plannedVerbs(PLANNED_ADMIN_VERBS),
|
|
182
196
|
],
|
|
197
|
+
commonTypedArgs: COMMON_TYPED_ARGS.filter(argument => argument.name !== "input"),
|
|
183
198
|
typedArgs: [
|
|
184
199
|
{ name: "quick", type: "boolean", appliesToVerbs: ["kickoff"] },
|
|
185
200
|
{ name: "standard", type: "boolean", appliesToVerbs: ["kickoff"] },
|
|
186
201
|
{ name: "deep", type: "boolean", appliesToVerbs: ["kickoff"] },
|
|
202
|
+
{ name: "trace", type: "boolean", appliesToVerbs: ["kickoff"] },
|
|
187
203
|
{ name: "threshold", type: "number", appliesToVerbs: ["kickoff"] },
|
|
188
204
|
{ name: "threshold-source", type: "string", appliesToVerbs: ["kickoff"] },
|
|
189
205
|
{ name: "stage", type: "enum", enumValues: ["final"], appliesToVerbs: ["write-spec"] },
|
|
190
206
|
{ name: "slug", type: "string", appliesToVerbs: ["write-spec"] },
|
|
207
|
+
{ name: "slug", type: "string", required: true, appliesToVerbs: ["crystallize"] },
|
|
191
208
|
{ name: "spec", type: "string", required: true, appliesToVerbs: ["write-spec"] },
|
|
192
209
|
{ name: "handoff", type: "enum", enumValues: ["ralplan"], appliesToVerbs: ["write-spec"] },
|
|
193
210
|
{ name: "deliberate", type: "boolean", appliesToVerbs: ["write-spec"] },
|
|
211
|
+
{ name: "force", type: "boolean", appliesToVerbs: ["write-spec"] },
|
|
194
212
|
{
|
|
195
213
|
name: "json",
|
|
196
214
|
type: "boolean",
|
|
197
|
-
appliesToVerbs: [
|
|
215
|
+
appliesToVerbs: [
|
|
216
|
+
"kickoff",
|
|
217
|
+
"write-spec",
|
|
218
|
+
"crystallize",
|
|
219
|
+
"stage",
|
|
220
|
+
"check",
|
|
221
|
+
"apply",
|
|
222
|
+
"discard",
|
|
223
|
+
"read",
|
|
224
|
+
"write",
|
|
225
|
+
"clear",
|
|
226
|
+
"handoff",
|
|
227
|
+
"approve-execution",
|
|
228
|
+
],
|
|
198
229
|
},
|
|
199
|
-
{ name: "input", type: "string",
|
|
230
|
+
{ name: "input", type: "string", appliesToVerbs: ["api"] },
|
|
231
|
+
{ name: "input", type: "string", required: true, appliesToVerbs: ["write", "stage", "crystallize"] },
|
|
232
|
+
{ name: "mode", type: "enum", enumValues: ["deep-interview"], appliesToVerbs: ["approve-execution"] },
|
|
233
|
+
{ name: "session-id", type: "string", appliesToVerbs: ["crystallize", "approve-execution"] },
|
|
200
234
|
{ name: "reset", type: "boolean", appliesToVerbs: ["write"] },
|
|
201
235
|
{
|
|
202
236
|
name: "for",
|
|
@@ -523,9 +557,8 @@ export function listVerbs(skill: CanonicalGjcWorkflowSkill): string[] {
|
|
|
523
557
|
}
|
|
524
558
|
|
|
525
559
|
export function typedArgsFor(skill: CanonicalGjcWorkflowSkill, verb: string): TypedArgSpec[] {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
);
|
|
560
|
+
const manifest = WORKFLOW_MANIFEST[skill];
|
|
561
|
+
return manifest.typedArgs.filter(arg => arg.appliesToVerbs === undefined || arg.appliesToVerbs.includes(verb));
|
|
529
562
|
}
|
|
530
563
|
|
|
531
564
|
function stableSort(value: unknown): unknown {
|
|
@@ -142,6 +142,7 @@ export async function operate(goal: string, opts: OperateOptions): Promise<Opera
|
|
|
142
142
|
lifecycle = "finalizing";
|
|
143
143
|
break;
|
|
144
144
|
}
|
|
145
|
+
lifecycle = accepted ? "observing" : "submitted";
|
|
145
146
|
continue;
|
|
146
147
|
}
|
|
147
148
|
|
|
@@ -182,11 +183,18 @@ export async function operate(goal: string, opts: OperateOptions): Promise<Opera
|
|
|
182
183
|
return { completed: false, lifecycle, iterations, classifications, vanishReceiptIds, blockers };
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
//
|
|
186
|
+
// An observation budget bounds this invocation, not the work itself. Keep active
|
|
187
|
+
// sessions nonterminal so a caller can resume observation without treating a slow
|
|
188
|
+
// but healthy owner as a failure. Completion still requires an explicit signal.
|
|
186
189
|
if (lifecycle !== "finalizing") {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
await emit("info", "operate_observation_window_ended", {
|
|
191
|
+
lifecycle,
|
|
192
|
+
iterations,
|
|
193
|
+
lastClassification: classifications.at(-1) ?? null,
|
|
194
|
+
completionObserved: false,
|
|
195
|
+
resumeWith: "observe",
|
|
196
|
+
});
|
|
197
|
+
return { completed: false, lifecycle, iterations, classifications, vanishReceiptIds, blockers };
|
|
190
198
|
}
|
|
191
199
|
|
|
192
200
|
const finalize = await runFinalize({
|
|
@@ -809,7 +809,11 @@ export class RuntimeOwner {
|
|
|
809
809
|
evidence: Record<string, unknown>,
|
|
810
810
|
): Promise<void> => {
|
|
811
811
|
if (signal.aborted) return;
|
|
812
|
-
if (kind === "
|
|
812
|
+
if (kind === "operate_observation_window_ended") {
|
|
813
|
+
const lifecycle = evidence.lifecycle === "submitted" ? "submitted" : "observing";
|
|
814
|
+
const persisted = await this.#persistValidationLifecycle(signal, lifecycle, []);
|
|
815
|
+
if (!persisted.persisted) return;
|
|
816
|
+
} else if (kind === "operate_blocked" || kind === "operate_finalized") {
|
|
813
817
|
const lifecycle = kind === "operate_finalized" && evidence.completed === true ? "completed" : "blocked";
|
|
814
818
|
const blockers = Array.isArray(evidence.blockers)
|
|
815
819
|
? evidence.blockers.filter((blocker): blocker is string => typeof blocker === "string")
|
package/src/hashline/parser.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ABORT_MARKER, ABORT_WARNING, BEGIN_PATCH_MARKER, END_PATCH_MARKER, RANG
|
|
|
2
2
|
import {
|
|
3
3
|
computeLineHash,
|
|
4
4
|
describeAnchorExamples,
|
|
5
|
+
HL_BODY_SEP,
|
|
5
6
|
HL_BODY_SEP_RE_RAW,
|
|
6
7
|
HL_FILE_PREFIX,
|
|
7
8
|
HL_HASH_CAPTURE_RE_RAW,
|
|
@@ -206,11 +207,43 @@ export function cloneCursor(cursor: HashlineCursor): HashlineCursor {
|
|
|
206
207
|
return cursor;
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
/** Context for diagnosing an insert op that ended up with no payload lines. */
|
|
211
|
+
interface EmptyInsertContext {
|
|
212
|
+
/** The op token as written, without any `|TEXT` body (e.g. `»300rk`). */
|
|
213
|
+
op: string;
|
|
214
|
+
/** True when the op line carried `|TEXT` that only echoed the anchored line. */
|
|
215
|
+
echoedAnchorText: boolean;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function formatEmptyInsertError(opLineNum: number, context: EmptyInsertContext): string {
|
|
219
|
+
const base = `line ${opLineNum}: ${HL_OP_INSERT_BEFORE} and ${HL_OP_INSERT_AFTER} operations require at least one verbatim payload line.`;
|
|
220
|
+
if (context.echoedAnchorText) {
|
|
221
|
+
return (
|
|
222
|
+
`${base} The text after "${HL_BODY_SEP}" on "${context.op}${HL_BODY_SEP}…" matches the supplied anchor hash, ` +
|
|
223
|
+
`so it was read as an anchor echo, not as new content. Put the lines to insert on the lines after "${context.op}".`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
return (
|
|
227
|
+
`${base} To insert a single blank line, put one empty line after "${context.op}" ` +
|
|
228
|
+
`(when it is the last op, the edit input ends with two newlines).`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Payload is required only when the op line carried no usable inline `|TEXT` body. */
|
|
233
|
+
function emptyInsertContext(
|
|
234
|
+
sigil: string,
|
|
235
|
+
match: RegExpExecArray,
|
|
236
|
+
inlineBody: string | undefined,
|
|
237
|
+
): EmptyInsertContext | undefined {
|
|
238
|
+
if (inlineBody !== undefined) return undefined;
|
|
239
|
+
return { op: `${sigil}${match[1]}`, echoedAnchorText: match[2] !== undefined };
|
|
240
|
+
}
|
|
241
|
+
|
|
209
242
|
function collectPayload(
|
|
210
243
|
lines: string[],
|
|
211
244
|
startIndex: number,
|
|
212
245
|
opLineNum: number,
|
|
213
|
-
|
|
246
|
+
emptyInsert: EmptyInsertContext | undefined,
|
|
214
247
|
): { payload: string[]; nextIndex: number } {
|
|
215
248
|
const payload: string[] = [];
|
|
216
249
|
let index = startIndex;
|
|
@@ -220,11 +253,7 @@ function collectPayload(
|
|
|
220
253
|
payload.push(line);
|
|
221
254
|
index++;
|
|
222
255
|
}
|
|
223
|
-
if (payload.length === 0 &&
|
|
224
|
-
throw new Error(
|
|
225
|
-
`line ${opLineNum}: ${HL_OP_INSERT_BEFORE} and ${HL_OP_INSERT_AFTER} operations require at least one verbatim payload line.`,
|
|
226
|
-
);
|
|
227
|
-
}
|
|
256
|
+
if (payload.length === 0 && emptyInsert) throw new Error(formatEmptyInsertError(opLineNum, emptyInsert));
|
|
228
257
|
return { payload, nextIndex: index };
|
|
229
258
|
}
|
|
230
259
|
|
|
@@ -267,7 +296,12 @@ export function parseHashlineWithWarnings(diff: string): { edits: HashlineEdit[]
|
|
|
267
296
|
if (insertBeforeMatch) {
|
|
268
297
|
const cursor = parseInsertTarget(insertBeforeMatch[1], lineNum, "before");
|
|
269
298
|
const inlineBody = resolveInlineInsertBody(cursor, insertBeforeMatch[2]);
|
|
270
|
-
const { payload, nextIndex } = collectPayload(
|
|
299
|
+
const { payload, nextIndex } = collectPayload(
|
|
300
|
+
lines,
|
|
301
|
+
i + 1,
|
|
302
|
+
lineNum,
|
|
303
|
+
emptyInsertContext(HL_OP_INSERT_BEFORE, insertBeforeMatch, inlineBody),
|
|
304
|
+
);
|
|
271
305
|
if (inlineBody !== undefined) pushInsert(cursor, inlineBody, lineNum);
|
|
272
306
|
for (const text of payload) pushInsert(cursor, text, lineNum);
|
|
273
307
|
i = nextIndex;
|
|
@@ -278,7 +312,12 @@ export function parseHashlineWithWarnings(diff: string): { edits: HashlineEdit[]
|
|
|
278
312
|
if (insertAfterMatch) {
|
|
279
313
|
const cursor = parseInsertTarget(insertAfterMatch[1], lineNum, "after");
|
|
280
314
|
const inlineBody = resolveInlineInsertBody(cursor, insertAfterMatch[2]);
|
|
281
|
-
const { payload, nextIndex } = collectPayload(
|
|
315
|
+
const { payload, nextIndex } = collectPayload(
|
|
316
|
+
lines,
|
|
317
|
+
i + 1,
|
|
318
|
+
lineNum,
|
|
319
|
+
emptyInsertContext(HL_OP_INSERT_AFTER, insertAfterMatch, inlineBody),
|
|
320
|
+
);
|
|
282
321
|
if (inlineBody !== undefined) pushInsert(cursor, inlineBody, lineNum);
|
|
283
322
|
for (const text of payload) pushInsert(cursor, text, lineNum);
|
|
284
323
|
i = nextIndex;
|
|
@@ -288,7 +327,7 @@ export function parseHashlineWithWarnings(diff: string): { edits: HashlineEdit[]
|
|
|
288
327
|
const replaceMatch = REPLACE_OP_RE.exec(line);
|
|
289
328
|
if (replaceMatch) {
|
|
290
329
|
const range = parseRange(replaceMatch[1], lineNum, HL_OP_REPLACE);
|
|
291
|
-
const { payload, nextIndex } = collectPayload(lines, i + 1, lineNum,
|
|
330
|
+
const { payload, nextIndex } = collectPayload(lines, i + 1, lineNum, undefined);
|
|
292
331
|
if (payload.length > 0) {
|
|
293
332
|
for (const text of payload) {
|
|
294
333
|
edits.push({
|