@gajae-code/coding-agent 0.2.5 → 0.3.1
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 +28 -0
- package/dist/types/async/job-manager.d.ts +91 -2
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +10 -4
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/debug/crash-diagnostics.d.ts +45 -0
- package/dist/types/debug/runtime-gauges.d.ts +6 -0
- package/dist/types/deep-interview/render-middleware.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/eval/py/kernel.d.ts +2 -0
- package/dist/types/exec/bash-executor.d.ts +10 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +33 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +147 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +31 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +23 -29
- package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
- package/dist/types/internal-urls/types.d.ts +4 -0
- package/dist/types/lsp/index.d.ts +10 -10
- package/dist/types/modes/bridge/auth.d.ts +12 -0
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
- package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
- package/dist/types/modes/bridge/event-stream.d.ts +8 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
- package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
- package/dist/types/modes/index.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/jobs-observer.d.ts +57 -0
- package/dist/types/modes/rpc/host-tools.d.ts +1 -16
- package/dist/types/modes/rpc/host-uris.d.ts +1 -38
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
- package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +19 -1
- package/dist/types/skill-state/active-state.d.ts +2 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +25 -2
- package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/id.d.ts +7 -0
- package/dist/types/task/index.d.ts +5 -0
- package/dist/types/task/receipt.d.ts +85 -0
- package/dist/types/task/spawn-gate.d.ts +38 -0
- package/dist/types/task/types.d.ts +198 -14
- package/dist/types/tools/cron.d.ts +6 -0
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +26 -1
- package/package.json +7 -7
- package/scripts/build-binary.ts +7 -0
- package/src/async/job-manager.ts +334 -6
- package/src/cli/args.ts +9 -2
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/deep-interview.ts +1 -0
- package/src/commands/harness.ts +862 -0
- package/src/commands/launch.ts +2 -2
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +54 -39
- package/src/config/keybindings.ts +6 -0
- package/src/config/settings-schema.ts +13 -3
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +17 -3
- package/src/debug/crash-diagnostics.ts +223 -0
- package/src/debug/runtime-gauges.ts +20 -0
- package/src/deep-interview/render-middleware.ts +372 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +106 -13
- package/src/eval/py/executor.ts +21 -1
- package/src/eval/py/kernel.ts +15 -0
- package/src/exec/bash-executor.ts +41 -0
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/cli-write-receipt.ts +31 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +98 -42
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +235 -43
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +179 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +1155 -46
- package/src/gjc-runtime/state-schema.ts +192 -0
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +749 -0
- package/src/gjc-runtime/team-runtime.ts +1255 -189
- package/src/gjc-runtime/ultragoal-runtime.ts +460 -43
- package/src/gjc-runtime/workflow-command-ref.ts +239 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +1601 -0
- package/src/gjc-runtime/workflow-manifest.ts +427 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +148 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +600 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +98 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +197 -64
- package/src/internal-urls/agent-protocol.ts +68 -21
- package/src/internal-urls/artifact-protocol.ts +12 -17
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/registry-helpers.ts +19 -16
- package/src/internal-urls/types.ts +4 -0
- package/src/lsp/client.ts +18 -2
- package/src/main.ts +21 -5
- package/src/modes/bridge/auth.ts +41 -0
- package/src/modes/bridge/bridge-client-bridge.ts +47 -0
- package/src/modes/bridge/bridge-mode.ts +520 -0
- package/src/modes/bridge/bridge-ui-context.ts +200 -0
- package/src/modes/bridge/event-stream.ts +70 -0
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/custom-editor.ts +101 -0
- package/src/modes/components/hook-selector.ts +133 -20
- package/src/modes/components/jobs-overlay-model.ts +109 -0
- package/src/modes/components/jobs-overlay.ts +172 -0
- package/src/modes/components/status-line/presets.ts +7 -5
- package/src/modes/components/status-line/segments.ts +25 -0
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/status-line.ts +9 -1
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +43 -1
- package/src/modes/controllers/input-controller.ts +105 -9
- package/src/modes/controllers/selector-controller.ts +31 -1
- package/src/modes/index.ts +1 -0
- package/src/modes/interactive-mode.ts +28 -0
- package/src/modes/jobs-observer.ts +204 -0
- package/src/modes/rpc/host-tools.ts +1 -186
- package/src/modes/rpc/host-uris.ts +1 -235
- package/src/modes/rpc/rpc-client.ts +25 -10
- package/src/modes/rpc/rpc-mode.ts +12 -381
- package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
- package/src/modes/shared/agent-wire/command-validation.ts +131 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
- package/src/modes/shared/agent-wire/handshake.ts +117 -0
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
- package/src/modes/shared/agent-wire/protocol.ts +96 -0
- package/src/modes/shared/agent-wire/responses.ts +17 -0
- package/src/modes/shared/agent-wire/scopes.ts +89 -0
- package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
- package/src/modes/shared/agent-wire/ui-result.ts +48 -0
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/tools/subagent.md +39 -4
- package/src/prompts/tools/task-summary.md +3 -9
- package/src/prompts/tools/task.md +5 -1
- package/src/sdk.ts +8 -0
- package/src/session/agent-session.ts +445 -71
- package/src/session/session-manager.ts +13 -1
- package/src/skill-state/active-state.ts +58 -65
- package/src/skill-state/deep-interview-mutation-guard.ts +114 -17
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +33 -4
- package/src/skill-state/workflow-state-version.ts +3 -0
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/task/executor.ts +79 -13
- package/src/task/id.ts +33 -0
- package/src/task/index.ts +376 -74
- package/src/task/output-manager.ts +5 -4
- package/src/task/receipt.ts +297 -0
- package/src/task/render.ts +54 -134
- package/src/task/spawn-gate.ts +132 -0
- package/src/task/types.ts +104 -10
- package/src/tools/ask.ts +88 -27
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash.ts +1 -1
- package/src/tools/cron.ts +48 -0
- package/src/tools/find.ts +4 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/search.ts +1 -0
- package/src/tools/skill.ts +6 -1
- package/src/tools/subagent.ts +423 -79
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical zod schemas for GJC workflow state (Workstream A, v4).
|
|
3
|
+
*
|
|
4
|
+
* Schemas are **lenient/additive** (`.passthrough()`): unknown keys are
|
|
5
|
+
* preserved, non-anchored fields are optional. This upholds the binding v2
|
|
6
|
+
* read contract — reads never reject evolving/old state. The strict
|
|
7
|
+
* `RequiredOnWriteEnvelopeSchema` is the WRITE-side gate (fail-closed), anchored
|
|
8
|
+
* to exactly what the sanctioned writer emits.
|
|
9
|
+
*
|
|
10
|
+
* These schemas describe the persisted `WorkflowStateReceipt`/envelope; they are
|
|
11
|
+
* a distinct concept from the `CliWriteReceipt` stdout presentation type.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from "node:fs/promises";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { WORKFLOW_STATE_VERSION } from "../skill-state/workflow-state-version";
|
|
16
|
+
|
|
17
|
+
const CANONICAL_GJC_WORKFLOW_SKILLS = ["deep-interview", "ralplan", "ultragoal", "team"] as const;
|
|
18
|
+
const skillEnum = z.enum([...CANONICAL_GJC_WORKFLOW_SKILLS]);
|
|
19
|
+
const ownerEnum = z.enum(["gjc-state-cli", "gjc-runtime", "gjc-hook"]);
|
|
20
|
+
const receiptStatusEnum = z.enum(["fresh", "stale"]);
|
|
21
|
+
|
|
22
|
+
export const WorkflowStateContentChecksumSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
algorithm: z.literal("sha256"),
|
|
25
|
+
value: z.string(),
|
|
26
|
+
covered_path: z.string(),
|
|
27
|
+
computed_at: z.string(),
|
|
28
|
+
})
|
|
29
|
+
.passthrough();
|
|
30
|
+
|
|
31
|
+
/** Lenient receipt schema for reads (mirrors WorkflowStateReceipt). */
|
|
32
|
+
export const WorkflowStateReceiptSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
version: z.number(),
|
|
35
|
+
skill: skillEnum,
|
|
36
|
+
owner: ownerEnum,
|
|
37
|
+
command: z.string(),
|
|
38
|
+
state_path: z.string(),
|
|
39
|
+
storage_path: z.string(),
|
|
40
|
+
mutated_at: z.string(),
|
|
41
|
+
fresh_until: z.string(),
|
|
42
|
+
status: receiptStatusEnum,
|
|
43
|
+
mutation_id: z.string(),
|
|
44
|
+
verb: z.string().optional(),
|
|
45
|
+
from_phase: z.string().optional(),
|
|
46
|
+
to_phase: z.string().optional(),
|
|
47
|
+
forced: z.boolean().optional(),
|
|
48
|
+
paths: z.array(z.string()).optional(),
|
|
49
|
+
content_sha256: WorkflowStateContentChecksumSchema.optional(),
|
|
50
|
+
})
|
|
51
|
+
.passthrough();
|
|
52
|
+
|
|
53
|
+
/** Lenient envelope schema for reads. Every non-structural field optional. */
|
|
54
|
+
export const WorkflowStateEnvelopeSchema = z
|
|
55
|
+
.object({
|
|
56
|
+
skill: z.string().optional(),
|
|
57
|
+
active: z.boolean().optional(),
|
|
58
|
+
current_phase: z.string().optional(),
|
|
59
|
+
version: z.number().optional(),
|
|
60
|
+
updated_at: z.string().optional(),
|
|
61
|
+
session_id: z.string().optional(),
|
|
62
|
+
receipt: WorkflowStateReceiptSchema.optional(),
|
|
63
|
+
})
|
|
64
|
+
.passthrough();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Strict receipt required on WRITE (post checksum-stamping). Anchored to the
|
|
68
|
+
* fields the sanctioned writer emits — `content_sha256` is REQUIRED here.
|
|
69
|
+
*/
|
|
70
|
+
export const RequiredWorkflowStateReceiptSchema = z
|
|
71
|
+
.object({
|
|
72
|
+
version: z.number(),
|
|
73
|
+
skill: skillEnum,
|
|
74
|
+
owner: ownerEnum,
|
|
75
|
+
command: z.string(),
|
|
76
|
+
state_path: z.string(),
|
|
77
|
+
storage_path: z.string(),
|
|
78
|
+
mutated_at: z.string(),
|
|
79
|
+
fresh_until: z.string(),
|
|
80
|
+
status: receiptStatusEnum,
|
|
81
|
+
mutation_id: z.string(),
|
|
82
|
+
content_sha256: WorkflowStateContentChecksumSchema,
|
|
83
|
+
})
|
|
84
|
+
.passthrough();
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Write-side fail-closed gate: the serialized on-disk envelope must satisfy
|
|
88
|
+
* this after checksum stamping. Anchored to current sanctioned-writer output.
|
|
89
|
+
*/
|
|
90
|
+
export const RequiredOnWriteEnvelopeSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
skill: skillEnum,
|
|
93
|
+
version: z.literal(WORKFLOW_STATE_VERSION),
|
|
94
|
+
updated_at: z.string(),
|
|
95
|
+
current_phase: z.string(),
|
|
96
|
+
active: z.boolean(),
|
|
97
|
+
receipt: RequiredWorkflowStateReceiptSchema,
|
|
98
|
+
})
|
|
99
|
+
.passthrough();
|
|
100
|
+
|
|
101
|
+
/** Per-skill mode state consumed by hooks / the mutation guard. */
|
|
102
|
+
export const ModeStateSchema = z
|
|
103
|
+
.object({
|
|
104
|
+
active: z.boolean().optional(),
|
|
105
|
+
current_phase: z.string().optional(),
|
|
106
|
+
skill: z.string().optional(),
|
|
107
|
+
session_id: z.string().optional(),
|
|
108
|
+
thread_id: z.string().optional(),
|
|
109
|
+
cwd: z.string().optional(),
|
|
110
|
+
updated_at: z.string().optional(),
|
|
111
|
+
handoff_from: z.string().optional(),
|
|
112
|
+
handoff_to: z.string().optional(),
|
|
113
|
+
handoff_at: z.string().optional(),
|
|
114
|
+
})
|
|
115
|
+
.passthrough();
|
|
116
|
+
|
|
117
|
+
export const SkillActiveEntrySchema = z
|
|
118
|
+
.object({
|
|
119
|
+
skill: z.string(),
|
|
120
|
+
phase: z.string().optional(),
|
|
121
|
+
active: z.boolean().optional(),
|
|
122
|
+
activated_at: z.string().optional(),
|
|
123
|
+
updated_at: z.string().optional(),
|
|
124
|
+
session_id: z.string().optional(),
|
|
125
|
+
thread_id: z.string().optional(),
|
|
126
|
+
turn_id: z.string().optional(),
|
|
127
|
+
stale: z.boolean().optional(),
|
|
128
|
+
handoff_from: z.string().optional(),
|
|
129
|
+
handoff_to: z.string().optional(),
|
|
130
|
+
handoff_at: z.string().optional(),
|
|
131
|
+
receipt: WorkflowStateReceiptSchema.optional(),
|
|
132
|
+
})
|
|
133
|
+
.passthrough();
|
|
134
|
+
|
|
135
|
+
export const SkillActiveStateSchema = z
|
|
136
|
+
.object({
|
|
137
|
+
version: z.number().optional(),
|
|
138
|
+
active: z.boolean().optional(),
|
|
139
|
+
skill: z.string().optional(),
|
|
140
|
+
keyword: z.string().optional(),
|
|
141
|
+
phase: z.string().optional(),
|
|
142
|
+
activated_at: z.string().optional(),
|
|
143
|
+
updated_at: z.string().optional(),
|
|
144
|
+
source: z.string().optional(),
|
|
145
|
+
session_id: z.string().optional(),
|
|
146
|
+
thread_id: z.string().optional(),
|
|
147
|
+
turn_id: z.string().optional(),
|
|
148
|
+
active_skills: z.array(SkillActiveEntrySchema).optional(),
|
|
149
|
+
})
|
|
150
|
+
.passthrough();
|
|
151
|
+
|
|
152
|
+
export type WorkflowStateEnvelope = z.infer<typeof WorkflowStateEnvelopeSchema>;
|
|
153
|
+
export type RequiredOnWriteEnvelope = z.infer<typeof RequiredOnWriteEnvelopeSchema>;
|
|
154
|
+
export type ModeStateParsed = z.infer<typeof ModeStateSchema>;
|
|
155
|
+
export type SkillActiveStateParsed = z.infer<typeof SkillActiveStateSchema>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Validated read result.
|
|
159
|
+
* - `null` → file absent (ENOENT); callers treat as no state.
|
|
160
|
+
* - `{ok:true}` → parsed + schema-valid.
|
|
161
|
+
* - `{ok:false}` → present but unparseable or schema-invalid. Callers fail
|
|
162
|
+
* OPEN (normalize/log), never crash — preserving v2 reads.
|
|
163
|
+
*/
|
|
164
|
+
export type ReadGjcJsonResult<T> = { ok: true; value: T; raw: unknown } | { ok: false; error: string; raw: unknown };
|
|
165
|
+
|
|
166
|
+
function isEnoent(error: unknown): boolean {
|
|
167
|
+
return Boolean(error) && (error as NodeJS.ErrnoException).code === "ENOENT";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Parse + schema-validate a `.gjc` JSON file at the read boundary.
|
|
172
|
+
* Returns `null` when the file is absent. Fail-open: an invalid file yields
|
|
173
|
+
* `{ ok: false }` with the raw value attached so the caller can normalize/log.
|
|
174
|
+
*/
|
|
175
|
+
export async function readGjcJson<T>(filePath: string, schema: z.ZodType<T>): Promise<ReadGjcJsonResult<T> | null> {
|
|
176
|
+
let text: string;
|
|
177
|
+
try {
|
|
178
|
+
text = await fs.readFile(filePath, "utf-8");
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (isEnoent(error)) return null;
|
|
181
|
+
return { ok: false, error: `read error: ${(error as Error).message}`, raw: undefined };
|
|
182
|
+
}
|
|
183
|
+
let raw: unknown;
|
|
184
|
+
try {
|
|
185
|
+
raw = JSON.parse(text);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return { ok: false, error: `invalid JSON: ${(error as Error).message}`, raw: text };
|
|
188
|
+
}
|
|
189
|
+
const parsed = schema.safeParse(raw);
|
|
190
|
+
if (parsed.success) return { ok: true, value: parsed.data, raw };
|
|
191
|
+
return { ok: false, error: parsed.error.message, raw };
|
|
192
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CanonicalGjcWorkflowSkill } from "../skill-state/active-state";
|
|
2
|
+
|
|
3
|
+
export interface StateValidationResult {
|
|
4
|
+
valid: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
9
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function typeName(value: unknown): string {
|
|
13
|
+
if (Array.isArray(value)) return "array";
|
|
14
|
+
if (value === null) return "null";
|
|
15
|
+
return typeof value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function validateWorkflowStateEnvelope(skill: CanonicalGjcWorkflowSkill, state: unknown): StateValidationResult {
|
|
19
|
+
if (!isPlainObject(state)) {
|
|
20
|
+
return { valid: false, error: `state for ${skill} must be a JSON object, got ${typeName(state)}` };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if ("skill" in state && state.skill !== skill) {
|
|
24
|
+
return { valid: false, error: `state skill must match selected mode ${skill}` };
|
|
25
|
+
}
|
|
26
|
+
if ("active" in state && typeof state.active !== "boolean") {
|
|
27
|
+
return { valid: false, error: `state.active must be a boolean when present, got ${typeName(state.active)}` };
|
|
28
|
+
}
|
|
29
|
+
if ("current_phase" in state && typeof state.current_phase !== "string") {
|
|
30
|
+
return {
|
|
31
|
+
valid: false,
|
|
32
|
+
error: `state.current_phase must be a string when present, got ${typeName(state.current_phase)}`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if ("version" in state && typeof state.version !== "number") {
|
|
36
|
+
return { valid: false, error: `state.version must be a number when present, got ${typeName(state.version)}` };
|
|
37
|
+
}
|
|
38
|
+
if ("updated_at" in state && typeof state.updated_at !== "string") {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
error: `state.updated_at must be a string when present, got ${typeName(state.updated_at)}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if ("receipt" in state && state.receipt !== undefined && !isPlainObject(state.receipt)) {
|
|
45
|
+
return { valid: false, error: `state.receipt must be an object when present, got ${typeName(state.receipt)}` };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { valid: true };
|
|
49
|
+
}
|