@jmanuelcorral/openteam 0.2.0 → 0.2.2
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.es.md +5 -5
- package/README.md +5 -5
- package/dist/cli/consoleServe.d.ts +9 -0
- package/dist/cli/consoleServe.d.ts.map +1 -1
- package/dist/cli/graphViewProvider.d.ts +30 -0
- package/dist/cli/graphViewProvider.d.ts.map +1 -0
- package/dist/cli.js +1271 -240
- package/dist/commands/dispatch.d.ts +1 -0
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/orchestratorAgent.d.ts.map +1 -1
- package/dist/config/graphFeatureGate.d.ts +8 -3
- package/dist/config/graphFeatureGate.d.ts.map +1 -1
- package/dist/config/schema.d.ts +5 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/context/redaction.d.ts +8 -0
- package/dist/context/redaction.d.ts.map +1 -1
- package/dist/graph/certificate.d.ts +12 -2
- package/dist/graph/certificate.d.ts.map +1 -1
- package/dist/graph/soakLedger.d.ts +25 -2
- package/dist/graph/soakLedger.d.ts.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3028 -1678
- package/dist/orchestrator/coordinator.d.ts +37 -2
- package/dist/orchestrator/coordinator.d.ts.map +1 -1
- package/dist/orchestrator/graphIngress.d.ts +1 -1
- package/dist/orchestrator/graphIngress.d.ts.map +1 -1
- package/dist/orchestrator/ledger.d.ts +11 -42
- package/dist/orchestrator/ledger.d.ts.map +1 -1
- package/dist/orchestrator/sddCompiler.d.ts +25 -1
- package/dist/orchestrator/sddCompiler.d.ts.map +1 -1
- package/dist/plugin/orchestrateTool.d.ts +197 -0
- package/dist/plugin/orchestrateTool.d.ts.map +1 -0
- package/dist/telemetry/events.d.ts +37 -0
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/orchestrator/sdd.d.ts +0 -180
- package/dist/orchestrator/sdd.d.ts.map +0 -1
- package/dist/storage/graph/migrateLegacyBriefs.d.ts +0 -32
- package/dist/storage/graph/migrateLegacyBriefs.d.ts.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -10,11 +10,187 @@ import { promisify } from "node:util";
|
|
|
10
10
|
// src/cli/consoleServe.ts
|
|
11
11
|
import { randomBytes } from "node:crypto";
|
|
12
12
|
|
|
13
|
-
// src/
|
|
13
|
+
// src/config/schema.ts
|
|
14
14
|
import { z } from "zod";
|
|
15
|
+
var ModelRefSchema = z.object({
|
|
16
|
+
providerID: z.string().min(1),
|
|
17
|
+
modelID: z.string().min(1)
|
|
18
|
+
});
|
|
19
|
+
var RouterModeSchema = z.enum(["economy", "balanced", "quality"]);
|
|
20
|
+
var PrivacyModeSchema = z.enum([
|
|
21
|
+
"forceLocalOnSensitive",
|
|
22
|
+
"consentBeforeFrontier",
|
|
23
|
+
"off"
|
|
24
|
+
]);
|
|
25
|
+
var BaselineModeSchema = z.enum(["auto", "pinned"]);
|
|
26
|
+
var GraphModeSchema = z.enum(["off", "shadow", "active"]);
|
|
27
|
+
var DEFAULT_GRAPH_JOURNAL_ROOT = ".opencode/openteam/graph/runs";
|
|
28
|
+
var GraphConfigSchema = z.object({
|
|
29
|
+
mode: GraphModeSchema.default("off"),
|
|
30
|
+
killSwitch: z.boolean().default(false),
|
|
31
|
+
journalRoot: z.string().min(1).default(DEFAULT_GRAPH_JOURNAL_ROOT),
|
|
32
|
+
operatorApproval: z.boolean().default(false),
|
|
33
|
+
worktrees: z.object({
|
|
34
|
+
enabled: z.boolean().default(false)
|
|
35
|
+
}).default({ enabled: false })
|
|
36
|
+
});
|
|
37
|
+
var ConsoleHostSchema = z.enum(["127.0.0.1", "localhost"]);
|
|
38
|
+
var ConsoleConfigSchema = z.object({
|
|
39
|
+
host: ConsoleHostSchema.default("127.0.0.1"),
|
|
40
|
+
port: z.number().int().min(1024).max(65535).default(4599),
|
|
41
|
+
autoPortFallback: z.boolean().default(true),
|
|
42
|
+
refreshMs: z.number().int().min(250).default(2000),
|
|
43
|
+
recentRoutes: z.number().int().positive().default(50),
|
|
44
|
+
openBrowser: z.boolean().default(false),
|
|
45
|
+
remoteStorage: z.boolean().default(false),
|
|
46
|
+
terminal: z.object({
|
|
47
|
+
enabled: z.boolean().default(true),
|
|
48
|
+
pty: z.boolean().optional()
|
|
49
|
+
}).default({ enabled: true }),
|
|
50
|
+
graphView: z.object({
|
|
51
|
+
enabled: z.boolean().default(false)
|
|
52
|
+
}).optional()
|
|
53
|
+
}).default({
|
|
54
|
+
host: "127.0.0.1",
|
|
55
|
+
port: 4599,
|
|
56
|
+
autoPortFallback: true,
|
|
57
|
+
refreshMs: 2000,
|
|
58
|
+
recentRoutes: 50,
|
|
59
|
+
openBrowser: false,
|
|
60
|
+
remoteStorage: false,
|
|
61
|
+
terminal: { enabled: true }
|
|
62
|
+
});
|
|
63
|
+
var StorageConfigSchema = z.object({
|
|
64
|
+
sqliteIndex: z.object({
|
|
65
|
+
enabled: z.boolean().default(false),
|
|
66
|
+
path: z.string().min(1).default(".opencode/openteam/index.sqlite")
|
|
67
|
+
}).default({ enabled: false, path: ".opencode/openteam/index.sqlite" })
|
|
68
|
+
}).default({
|
|
69
|
+
sqliteIndex: { enabled: false, path: ".opencode/openteam/index.sqlite" }
|
|
70
|
+
});
|
|
71
|
+
var defaultLocalModel = {
|
|
72
|
+
providerID: "ollama",
|
|
73
|
+
modelID: "qwen3:8b"
|
|
74
|
+
};
|
|
75
|
+
var defaultFrontierModel = {
|
|
76
|
+
providerID: "anthropic",
|
|
77
|
+
modelID: "claude-sonnet-4-5"
|
|
78
|
+
};
|
|
79
|
+
var LocalRuntimeSchema = z.object({
|
|
80
|
+
id: z.enum(["ollama", "lmstudio", "foundry-local"]),
|
|
81
|
+
enabled: z.boolean().default(true),
|
|
82
|
+
baseURL: z.string().url().optional(),
|
|
83
|
+
discovery: z.enum(["cli", "sdk", "manual"]).optional(),
|
|
84
|
+
defaultModel: ModelRefSchema
|
|
85
|
+
});
|
|
86
|
+
var MemoryScopeSchema = z.enum(["project", "user"]);
|
|
87
|
+
var MemoryExtractionModeSchema = z.enum([
|
|
88
|
+
"local",
|
|
89
|
+
"localWithFrontierFallback"
|
|
90
|
+
]);
|
|
91
|
+
var MemoryEmbeddingsProviderSchema = z.enum(["local", "none"]);
|
|
92
|
+
var MemorySemanticSchema = z.object({
|
|
93
|
+
enabled: z.boolean().default(false),
|
|
94
|
+
storeContent: z.boolean().default(false),
|
|
95
|
+
logPath: z.string().min(1).default(".opencode/openteam/memory/records.jsonl"),
|
|
96
|
+
indexPath: z.string().min(1).default(".opencode/openteam/memory/index.sqlite"),
|
|
97
|
+
scope: MemoryScopeSchema.default("project"),
|
|
98
|
+
extraction: z.object({
|
|
99
|
+
mode: MemoryExtractionModeSchema.default("local"),
|
|
100
|
+
model: ModelRefSchema.nullable().default(null)
|
|
101
|
+
}).default({ mode: "local", model: null }),
|
|
102
|
+
embeddings: z.object({
|
|
103
|
+
provider: MemoryEmbeddingsProviderSchema.default("local"),
|
|
104
|
+
model: ModelRefSchema.nullable().default(null),
|
|
105
|
+
dim: z.number().int().positive().default(768)
|
|
106
|
+
}).default({ provider: "local", model: null, dim: 768 }),
|
|
107
|
+
recall: z.object({
|
|
108
|
+
maxResults: z.number().int().positive().default(8),
|
|
109
|
+
minSimilarity: z.number().min(0).max(1).default(0.2)
|
|
110
|
+
}).default({ maxResults: 8, minSimilarity: 0.2 }),
|
|
111
|
+
injection: z.object({
|
|
112
|
+
enabled: z.boolean().default(false),
|
|
113
|
+
maxChars: z.number().int().positive().default(1200),
|
|
114
|
+
maxItems: z.number().int().positive().default(8)
|
|
115
|
+
}).default({ enabled: false, maxChars: 1200, maxItems: 8 })
|
|
116
|
+
}).default({
|
|
117
|
+
enabled: false,
|
|
118
|
+
storeContent: false,
|
|
119
|
+
logPath: ".opencode/openteam/memory/records.jsonl",
|
|
120
|
+
indexPath: ".opencode/openteam/memory/index.sqlite",
|
|
121
|
+
scope: "project",
|
|
122
|
+
extraction: { mode: "local", model: null },
|
|
123
|
+
embeddings: { provider: "local", model: null, dim: 768 },
|
|
124
|
+
recall: { maxResults: 8, minSimilarity: 0.2 },
|
|
125
|
+
injection: { enabled: false, maxChars: 1200, maxItems: 8 }
|
|
126
|
+
});
|
|
127
|
+
var MemoryConfigSchema = z.object({
|
|
128
|
+
semantic: MemorySemanticSchema
|
|
129
|
+
});
|
|
130
|
+
var OpenTeamConfigObjectSchema = z.object({
|
|
131
|
+
baseline: z.object({
|
|
132
|
+
mode: BaselineModeSchema.default("auto"),
|
|
133
|
+
pinnedModel: ModelRefSchema.nullable().default(null),
|
|
134
|
+
hardDefault: ModelRefSchema.default(defaultFrontierModel)
|
|
135
|
+
}).default({
|
|
136
|
+
mode: "auto",
|
|
137
|
+
pinnedModel: null,
|
|
138
|
+
hardDefault: defaultFrontierModel
|
|
139
|
+
}),
|
|
140
|
+
router: z.object({
|
|
141
|
+
mode: RouterModeSchema.default("balanced"),
|
|
142
|
+
localDefault: ModelRefSchema.default(defaultLocalModel),
|
|
143
|
+
trivialPromptMaxChars: z.number().int().positive().default(280),
|
|
144
|
+
frontierPromptMinChars: z.number().int().positive().default(2000),
|
|
145
|
+
frontierOnly: z.boolean().default(false)
|
|
146
|
+
}).default({
|
|
147
|
+
mode: "balanced",
|
|
148
|
+
localDefault: defaultLocalModel,
|
|
149
|
+
trivialPromptMaxChars: 280,
|
|
150
|
+
frontierPromptMinChars: 2000,
|
|
151
|
+
frontierOnly: false
|
|
152
|
+
}),
|
|
153
|
+
local: z.object({
|
|
154
|
+
runtimes: z.array(LocalRuntimeSchema).min(1).default([
|
|
155
|
+
{
|
|
156
|
+
id: "ollama",
|
|
157
|
+
enabled: true,
|
|
158
|
+
baseURL: "http://localhost:11434/v1",
|
|
159
|
+
defaultModel: { providerID: "ollama", modelID: "qwen3:8b" }
|
|
160
|
+
}
|
|
161
|
+
])
|
|
162
|
+
}).default({
|
|
163
|
+
runtimes: [
|
|
164
|
+
{
|
|
165
|
+
id: "ollama",
|
|
166
|
+
enabled: true,
|
|
167
|
+
baseURL: "http://localhost:11434/v1",
|
|
168
|
+
defaultModel: defaultLocalModel
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
}),
|
|
172
|
+
budgets: z.object({
|
|
173
|
+
sessionUSD: z.number().positive().optional(),
|
|
174
|
+
monthlyUSD: z.number().positive().optional(),
|
|
175
|
+
frontierTokensPerSession: z.number().int().positive().optional(),
|
|
176
|
+
hardStopOnBudgetExhaustion: z.boolean().default(false)
|
|
177
|
+
}).default({ hardStopOnBudgetExhaustion: false }),
|
|
178
|
+
privacyMode: PrivacyModeSchema.default("forceLocalOnSensitive"),
|
|
179
|
+
console: ConsoleConfigSchema,
|
|
180
|
+
storage: StorageConfigSchema,
|
|
181
|
+
memory: MemoryConfigSchema.optional(),
|
|
182
|
+
graph: GraphConfigSchema.optional()
|
|
183
|
+
});
|
|
184
|
+
var OpenTeamConfigSchema = OpenTeamConfigObjectSchema;
|
|
185
|
+
function resolveMemorySemantic(config) {
|
|
186
|
+
return config.memory?.semantic ?? MemorySemanticSchema.parse(undefined);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/console/protocol.ts
|
|
190
|
+
import { z as z2 } from "zod";
|
|
15
191
|
var MAX_INPUT_CHARS = 1e5;
|
|
16
|
-
var SessionInputSchema =
|
|
17
|
-
text:
|
|
192
|
+
var SessionInputSchema = z2.object({
|
|
193
|
+
text: z2.string().min(1).max(MAX_INPUT_CHARS)
|
|
18
194
|
});
|
|
19
195
|
function parseSessionInput(raw) {
|
|
20
196
|
const result = SessionInputSchema.safeParse(raw);
|
|
@@ -623,214 +799,40 @@ function splitVirtualPath(path) {
|
|
|
623
799
|
import { z as z4 } from "zod";
|
|
624
800
|
|
|
625
801
|
// src/capabilities/types.ts
|
|
626
|
-
import { z as
|
|
627
|
-
var CapabilityTierSchema =
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
802
|
+
import { z as z3 } from "zod";
|
|
803
|
+
var CapabilityTierSchema = z3.union([
|
|
804
|
+
z3.literal(0),
|
|
805
|
+
z3.literal(1),
|
|
806
|
+
z3.literal(2),
|
|
807
|
+
z3.literal(3),
|
|
808
|
+
z3.literal(4),
|
|
809
|
+
z3.literal(5)
|
|
634
810
|
]);
|
|
635
|
-
var ComplexityTierSchema =
|
|
811
|
+
var ComplexityTierSchema = z3.enum([
|
|
636
812
|
"trivial",
|
|
637
813
|
"simple",
|
|
638
814
|
"moderate",
|
|
639
815
|
"hard"
|
|
640
816
|
]);
|
|
641
|
-
var ModelCapabilityProfileSchema =
|
|
642
|
-
ref:
|
|
643
|
-
providerID:
|
|
644
|
-
modelID:
|
|
817
|
+
var ModelCapabilityProfileSchema = z3.object({
|
|
818
|
+
ref: z3.object({
|
|
819
|
+
providerID: z3.string().min(1),
|
|
820
|
+
modelID: z3.string().min(1)
|
|
645
821
|
}),
|
|
646
|
-
kind:
|
|
647
|
-
contextWindow:
|
|
648
|
-
maxOutputTokens:
|
|
649
|
-
supportsToolCalling:
|
|
650
|
-
supportsVision:
|
|
822
|
+
kind: z3.enum(["local", "frontier", "router"]),
|
|
823
|
+
contextWindow: z3.number().int().positive(),
|
|
824
|
+
maxOutputTokens: z3.number().int().positive(),
|
|
825
|
+
supportsToolCalling: z3.boolean(),
|
|
826
|
+
supportsVision: z3.boolean(),
|
|
651
827
|
reasoningTier: CapabilityTierSchema,
|
|
652
828
|
codeQualityTier: CapabilityTierSchema,
|
|
653
|
-
costPer1M:
|
|
654
|
-
inputUSD:
|
|
655
|
-
outputUSD:
|
|
829
|
+
costPer1M: z3.object({
|
|
830
|
+
inputUSD: z3.number().min(0),
|
|
831
|
+
outputUSD: z3.number().min(0)
|
|
656
832
|
}),
|
|
657
|
-
availability:
|
|
833
|
+
availability: z3.enum(["available", "degraded", "unavailable"])
|
|
658
834
|
});
|
|
659
835
|
|
|
660
|
-
// src/config/schema.ts
|
|
661
|
-
import { z as z3 } from "zod";
|
|
662
|
-
var ModelRefSchema = z3.object({
|
|
663
|
-
providerID: z3.string().min(1),
|
|
664
|
-
modelID: z3.string().min(1)
|
|
665
|
-
});
|
|
666
|
-
var RouterModeSchema = z3.enum(["economy", "balanced", "quality"]);
|
|
667
|
-
var PrivacyModeSchema = z3.enum([
|
|
668
|
-
"forceLocalOnSensitive",
|
|
669
|
-
"consentBeforeFrontier",
|
|
670
|
-
"off"
|
|
671
|
-
]);
|
|
672
|
-
var BaselineModeSchema = z3.enum(["auto", "pinned"]);
|
|
673
|
-
var GraphModeSchema = z3.enum(["off", "shadow", "active"]);
|
|
674
|
-
var GraphConfigSchema = z3.object({
|
|
675
|
-
mode: GraphModeSchema.default("off"),
|
|
676
|
-
killSwitch: z3.boolean().default(false),
|
|
677
|
-
operatorApproval: z3.boolean().default(false),
|
|
678
|
-
worktrees: z3.object({
|
|
679
|
-
enabled: z3.boolean().default(false)
|
|
680
|
-
}).default({ enabled: false })
|
|
681
|
-
});
|
|
682
|
-
var ConsoleHostSchema = z3.enum(["127.0.0.1", "localhost"]);
|
|
683
|
-
var ConsoleConfigSchema = z3.object({
|
|
684
|
-
host: ConsoleHostSchema.default("127.0.0.1"),
|
|
685
|
-
port: z3.number().int().min(1024).max(65535).default(4599),
|
|
686
|
-
autoPortFallback: z3.boolean().default(true),
|
|
687
|
-
refreshMs: z3.number().int().min(250).default(2000),
|
|
688
|
-
recentRoutes: z3.number().int().positive().default(50),
|
|
689
|
-
openBrowser: z3.boolean().default(false),
|
|
690
|
-
remoteStorage: z3.boolean().default(false),
|
|
691
|
-
terminal: z3.object({
|
|
692
|
-
enabled: z3.boolean().default(true),
|
|
693
|
-
pty: z3.boolean().optional()
|
|
694
|
-
}).default({ enabled: true }),
|
|
695
|
-
graphView: z3.object({
|
|
696
|
-
enabled: z3.boolean().default(false)
|
|
697
|
-
}).optional()
|
|
698
|
-
}).default({
|
|
699
|
-
host: "127.0.0.1",
|
|
700
|
-
port: 4599,
|
|
701
|
-
autoPortFallback: true,
|
|
702
|
-
refreshMs: 2000,
|
|
703
|
-
recentRoutes: 50,
|
|
704
|
-
openBrowser: false,
|
|
705
|
-
remoteStorage: false,
|
|
706
|
-
terminal: { enabled: true }
|
|
707
|
-
});
|
|
708
|
-
var StorageConfigSchema = z3.object({
|
|
709
|
-
sqliteIndex: z3.object({
|
|
710
|
-
enabled: z3.boolean().default(false),
|
|
711
|
-
path: z3.string().min(1).default(".opencode/openteam/index.sqlite")
|
|
712
|
-
}).default({ enabled: false, path: ".opencode/openteam/index.sqlite" })
|
|
713
|
-
}).default({
|
|
714
|
-
sqliteIndex: { enabled: false, path: ".opencode/openteam/index.sqlite" }
|
|
715
|
-
});
|
|
716
|
-
var defaultLocalModel = {
|
|
717
|
-
providerID: "ollama",
|
|
718
|
-
modelID: "qwen3:8b"
|
|
719
|
-
};
|
|
720
|
-
var defaultFrontierModel = {
|
|
721
|
-
providerID: "anthropic",
|
|
722
|
-
modelID: "claude-sonnet-4-5"
|
|
723
|
-
};
|
|
724
|
-
var LocalRuntimeSchema = z3.object({
|
|
725
|
-
id: z3.enum(["ollama", "lmstudio", "foundry-local"]),
|
|
726
|
-
enabled: z3.boolean().default(true),
|
|
727
|
-
baseURL: z3.string().url().optional(),
|
|
728
|
-
discovery: z3.enum(["cli", "sdk", "manual"]).optional(),
|
|
729
|
-
defaultModel: ModelRefSchema
|
|
730
|
-
});
|
|
731
|
-
var MemoryScopeSchema = z3.enum(["project", "user"]);
|
|
732
|
-
var MemoryExtractionModeSchema = z3.enum([
|
|
733
|
-
"local",
|
|
734
|
-
"localWithFrontierFallback"
|
|
735
|
-
]);
|
|
736
|
-
var MemoryEmbeddingsProviderSchema = z3.enum(["local", "none"]);
|
|
737
|
-
var MemorySemanticSchema = z3.object({
|
|
738
|
-
enabled: z3.boolean().default(false),
|
|
739
|
-
storeContent: z3.boolean().default(false),
|
|
740
|
-
logPath: z3.string().min(1).default(".opencode/openteam/memory/records.jsonl"),
|
|
741
|
-
indexPath: z3.string().min(1).default(".opencode/openteam/memory/index.sqlite"),
|
|
742
|
-
scope: MemoryScopeSchema.default("project"),
|
|
743
|
-
extraction: z3.object({
|
|
744
|
-
mode: MemoryExtractionModeSchema.default("local"),
|
|
745
|
-
model: ModelRefSchema.nullable().default(null)
|
|
746
|
-
}).default({ mode: "local", model: null }),
|
|
747
|
-
embeddings: z3.object({
|
|
748
|
-
provider: MemoryEmbeddingsProviderSchema.default("local"),
|
|
749
|
-
model: ModelRefSchema.nullable().default(null),
|
|
750
|
-
dim: z3.number().int().positive().default(768)
|
|
751
|
-
}).default({ provider: "local", model: null, dim: 768 }),
|
|
752
|
-
recall: z3.object({
|
|
753
|
-
maxResults: z3.number().int().positive().default(8),
|
|
754
|
-
minSimilarity: z3.number().min(0).max(1).default(0.2)
|
|
755
|
-
}).default({ maxResults: 8, minSimilarity: 0.2 }),
|
|
756
|
-
injection: z3.object({
|
|
757
|
-
enabled: z3.boolean().default(false),
|
|
758
|
-
maxChars: z3.number().int().positive().default(1200),
|
|
759
|
-
maxItems: z3.number().int().positive().default(8)
|
|
760
|
-
}).default({ enabled: false, maxChars: 1200, maxItems: 8 })
|
|
761
|
-
}).default({
|
|
762
|
-
enabled: false,
|
|
763
|
-
storeContent: false,
|
|
764
|
-
logPath: ".opencode/openteam/memory/records.jsonl",
|
|
765
|
-
indexPath: ".opencode/openteam/memory/index.sqlite",
|
|
766
|
-
scope: "project",
|
|
767
|
-
extraction: { mode: "local", model: null },
|
|
768
|
-
embeddings: { provider: "local", model: null, dim: 768 },
|
|
769
|
-
recall: { maxResults: 8, minSimilarity: 0.2 },
|
|
770
|
-
injection: { enabled: false, maxChars: 1200, maxItems: 8 }
|
|
771
|
-
});
|
|
772
|
-
var MemoryConfigSchema = z3.object({
|
|
773
|
-
semantic: MemorySemanticSchema
|
|
774
|
-
});
|
|
775
|
-
var OpenTeamConfigObjectSchema = z3.object({
|
|
776
|
-
baseline: z3.object({
|
|
777
|
-
mode: BaselineModeSchema.default("auto"),
|
|
778
|
-
pinnedModel: ModelRefSchema.nullable().default(null),
|
|
779
|
-
hardDefault: ModelRefSchema.default(defaultFrontierModel)
|
|
780
|
-
}).default({
|
|
781
|
-
mode: "auto",
|
|
782
|
-
pinnedModel: null,
|
|
783
|
-
hardDefault: defaultFrontierModel
|
|
784
|
-
}),
|
|
785
|
-
router: z3.object({
|
|
786
|
-
mode: RouterModeSchema.default("balanced"),
|
|
787
|
-
localDefault: ModelRefSchema.default(defaultLocalModel),
|
|
788
|
-
trivialPromptMaxChars: z3.number().int().positive().default(280),
|
|
789
|
-
frontierPromptMinChars: z3.number().int().positive().default(2000),
|
|
790
|
-
frontierOnly: z3.boolean().default(false)
|
|
791
|
-
}).default({
|
|
792
|
-
mode: "balanced",
|
|
793
|
-
localDefault: defaultLocalModel,
|
|
794
|
-
trivialPromptMaxChars: 280,
|
|
795
|
-
frontierPromptMinChars: 2000,
|
|
796
|
-
frontierOnly: false
|
|
797
|
-
}),
|
|
798
|
-
local: z3.object({
|
|
799
|
-
runtimes: z3.array(LocalRuntimeSchema).min(1).default([
|
|
800
|
-
{
|
|
801
|
-
id: "ollama",
|
|
802
|
-
enabled: true,
|
|
803
|
-
baseURL: "http://localhost:11434/v1",
|
|
804
|
-
defaultModel: { providerID: "ollama", modelID: "qwen3:8b" }
|
|
805
|
-
}
|
|
806
|
-
])
|
|
807
|
-
}).default({
|
|
808
|
-
runtimes: [
|
|
809
|
-
{
|
|
810
|
-
id: "ollama",
|
|
811
|
-
enabled: true,
|
|
812
|
-
baseURL: "http://localhost:11434/v1",
|
|
813
|
-
defaultModel: defaultLocalModel
|
|
814
|
-
}
|
|
815
|
-
]
|
|
816
|
-
}),
|
|
817
|
-
budgets: z3.object({
|
|
818
|
-
sessionUSD: z3.number().positive().optional(),
|
|
819
|
-
monthlyUSD: z3.number().positive().optional(),
|
|
820
|
-
frontierTokensPerSession: z3.number().int().positive().optional(),
|
|
821
|
-
hardStopOnBudgetExhaustion: z3.boolean().default(false)
|
|
822
|
-
}).default({ hardStopOnBudgetExhaustion: false }),
|
|
823
|
-
privacyMode: PrivacyModeSchema.default("forceLocalOnSensitive"),
|
|
824
|
-
console: ConsoleConfigSchema,
|
|
825
|
-
storage: StorageConfigSchema,
|
|
826
|
-
memory: MemoryConfigSchema.optional(),
|
|
827
|
-
graph: GraphConfigSchema.optional()
|
|
828
|
-
});
|
|
829
|
-
var OpenTeamConfigSchema = OpenTeamConfigObjectSchema;
|
|
830
|
-
function resolveMemorySemantic(config) {
|
|
831
|
-
return config.memory?.semantic ?? MemorySemanticSchema.parse(undefined);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
836
|
// src/telemetry/events.ts
|
|
835
837
|
var EVENT_SCHEMA_VERSION = 1;
|
|
836
838
|
var EventBaseSchema = z4.object({
|
|
@@ -910,6 +912,17 @@ var SessionEndpointEventSchema = EventBaseSchema.extend({
|
|
|
910
912
|
worktree: z4.string().min(1).optional(),
|
|
911
913
|
title: z4.string().min(1).optional()
|
|
912
914
|
});
|
|
915
|
+
var ShadowDiagnosticEventSchema = EventBaseSchema.extend({
|
|
916
|
+
type: z4.literal("shadow-diagnostic"),
|
|
917
|
+
batchID: z4.string().min(1),
|
|
918
|
+
failureClass: z4.enum([
|
|
919
|
+
"invalid-node-id",
|
|
920
|
+
"journal-create-failed",
|
|
921
|
+
"journal-append-failed",
|
|
922
|
+
"observation-failed",
|
|
923
|
+
"unknown"
|
|
924
|
+
])
|
|
925
|
+
});
|
|
913
926
|
var OpenTeamEventSchema = z4.discriminatedUnion("type", [
|
|
914
927
|
RouteEventSchema,
|
|
915
928
|
MessageEventSchema,
|
|
@@ -917,7 +930,8 @@ var OpenTeamEventSchema = z4.discriminatedUnion("type", [
|
|
|
917
930
|
MeetingEventSchema,
|
|
918
931
|
DecisionEventSchema,
|
|
919
932
|
ActivityEventSchema,
|
|
920
|
-
SessionEndpointEventSchema
|
|
933
|
+
SessionEndpointEventSchema,
|
|
934
|
+
ShadowDiagnosticEventSchema
|
|
921
935
|
]);
|
|
922
936
|
|
|
923
937
|
// src/storage/fsStorageProvider.ts
|
|
@@ -3301,6 +3315,875 @@ async function createConsoleRuntime(deps) {
|
|
|
3301
3315
|
};
|
|
3302
3316
|
}
|
|
3303
3317
|
|
|
3318
|
+
// src/cli/graphViewProvider.ts
|
|
3319
|
+
import { readdirSync, statSync } from "node:fs";
|
|
3320
|
+
import { join as join3 } from "node:path";
|
|
3321
|
+
|
|
3322
|
+
// src/storage/graph/fsGraphJournal.ts
|
|
3323
|
+
import { join as join2 } from "node:path";
|
|
3324
|
+
|
|
3325
|
+
// src/context/redaction.ts
|
|
3326
|
+
import { createHash } from "node:crypto";
|
|
3327
|
+
|
|
3328
|
+
class RedactionError extends Error {
|
|
3329
|
+
code;
|
|
3330
|
+
key;
|
|
3331
|
+
constructor(key) {
|
|
3332
|
+
super(`raw content field is not allowed: "${key}"`);
|
|
3333
|
+
this.name = "RedactionError";
|
|
3334
|
+
this.code = "raw-content";
|
|
3335
|
+
this.key = key;
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
function sha256Hex(input) {
|
|
3339
|
+
return createHash("sha256").update(input, "utf8").digest("hex");
|
|
3340
|
+
}
|
|
3341
|
+
function redactedRef(ref) {
|
|
3342
|
+
return { uri: ref.uri, sha256: ref.sha256, bytes: ref.bytes };
|
|
3343
|
+
}
|
|
3344
|
+
var FORBIDDEN_RAW_KEYS = new Set([
|
|
3345
|
+
"content",
|
|
3346
|
+
"text",
|
|
3347
|
+
"brief",
|
|
3348
|
+
"prompt",
|
|
3349
|
+
"raw",
|
|
3350
|
+
"body",
|
|
3351
|
+
"output",
|
|
3352
|
+
"findings",
|
|
3353
|
+
"message",
|
|
3354
|
+
"source"
|
|
3355
|
+
]);
|
|
3356
|
+
function walkForbiddenKeys(value, onForbidden) {
|
|
3357
|
+
if (Array.isArray(value)) {
|
|
3358
|
+
for (const item of value) {
|
|
3359
|
+
walkForbiddenKeys(item, onForbidden);
|
|
3360
|
+
}
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
3363
|
+
if (value === null || typeof value !== "object") {
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
for (const [key, child] of Object.entries(value)) {
|
|
3367
|
+
if (FORBIDDEN_RAW_KEYS.has(key)) {
|
|
3368
|
+
const shouldStop = onForbidden(key);
|
|
3369
|
+
if (shouldStop)
|
|
3370
|
+
return;
|
|
3371
|
+
}
|
|
3372
|
+
walkForbiddenKeys(child, onForbidden);
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
function assertReferenceOnly(value) {
|
|
3376
|
+
walkForbiddenKeys(value, (key) => {
|
|
3377
|
+
throw new RedactionError(key);
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
// src/graph/types.ts
|
|
3382
|
+
var TERMINAL_NODE_STATUSES = new Set([
|
|
3383
|
+
"succeeded",
|
|
3384
|
+
"cancelled"
|
|
3385
|
+
]);
|
|
3386
|
+
var TERMINAL_RUN_STATUSES = new Set([
|
|
3387
|
+
"completed",
|
|
3388
|
+
"failed",
|
|
3389
|
+
"cancelled"
|
|
3390
|
+
]);
|
|
3391
|
+
|
|
3392
|
+
// src/graph/reducer.ts
|
|
3393
|
+
class ReducerError extends Error {
|
|
3394
|
+
code;
|
|
3395
|
+
detail;
|
|
3396
|
+
constructor(code, detail) {
|
|
3397
|
+
super(`${code}: ${detail}`);
|
|
3398
|
+
this.name = "ReducerError";
|
|
3399
|
+
this.code = code;
|
|
3400
|
+
this.detail = detail;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
function initialState(spec) {
|
|
3404
|
+
const nodes = {};
|
|
3405
|
+
for (const node of spec.nodes) {
|
|
3406
|
+
nodes[node.id] = { status: "pending", attempts: 0 };
|
|
3407
|
+
}
|
|
3408
|
+
return { runID: spec.runID, status: "running", seq: -1, nodes };
|
|
3409
|
+
}
|
|
3410
|
+
function withNode(state, nodeID, next) {
|
|
3411
|
+
return {
|
|
3412
|
+
...state,
|
|
3413
|
+
nodes: { ...state.nodes, [nodeID]: next },
|
|
3414
|
+
seq: state.seq
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
function specNode(spec, nodeID) {
|
|
3418
|
+
return spec.nodes.find((node) => node.id === nodeID);
|
|
3419
|
+
}
|
|
3420
|
+
function applyRunStarted(state, seq, specDigest) {
|
|
3421
|
+
if (state.specDigest !== undefined) {
|
|
3422
|
+
if (state.specDigest === specDigest) {
|
|
3423
|
+
return state;
|
|
3424
|
+
}
|
|
3425
|
+
throw new ReducerError("run-started-conflict", specDigest);
|
|
3426
|
+
}
|
|
3427
|
+
return { ...state, specDigest, seq };
|
|
3428
|
+
}
|
|
3429
|
+
function applyDispatched(spec, state, event) {
|
|
3430
|
+
const node = specNode(spec, event.nodeID);
|
|
3431
|
+
const current = state.nodes[event.nodeID];
|
|
3432
|
+
if (node === undefined || current === undefined) {
|
|
3433
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3434
|
+
}
|
|
3435
|
+
for (const dep of node.dependsOn) {
|
|
3436
|
+
if (state.nodes[dep]?.status !== "succeeded") {
|
|
3437
|
+
throw new ReducerError("dependency-not-satisfied", `${event.nodeID} <- ${dep}`);
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
const running = {
|
|
3441
|
+
status: "running",
|
|
3442
|
+
attempts: current.attempts + 1,
|
|
3443
|
+
operationID: event.operationID,
|
|
3444
|
+
attemptID: event.attemptID
|
|
3445
|
+
};
|
|
3446
|
+
if (current.status === "pending") {
|
|
3447
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, running);
|
|
3448
|
+
}
|
|
3449
|
+
if (current.status === "failed") {
|
|
3450
|
+
if (current.operationID !== event.operationID) {
|
|
3451
|
+
throw new ReducerError("dispatch-conflict", event.nodeID);
|
|
3452
|
+
}
|
|
3453
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, running);
|
|
3454
|
+
}
|
|
3455
|
+
if (current.status === "running") {
|
|
3456
|
+
if (current.operationID === event.operationID && current.attemptID === event.attemptID) {
|
|
3457
|
+
return state;
|
|
3458
|
+
}
|
|
3459
|
+
throw new ReducerError("dispatch-conflict", event.nodeID);
|
|
3460
|
+
}
|
|
3461
|
+
throw new ReducerError("illegal-transition", `dispatch ${current.status}`);
|
|
3462
|
+
}
|
|
3463
|
+
function applySucceeded(state, event) {
|
|
3464
|
+
const current = state.nodes[event.nodeID];
|
|
3465
|
+
if (current === undefined) {
|
|
3466
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3467
|
+
}
|
|
3468
|
+
if (current.status === "succeeded") {
|
|
3469
|
+
if (current.operationID === event.operationID && current.artifactSha256 === event.artifact.sha256) {
|
|
3470
|
+
return state;
|
|
3471
|
+
}
|
|
3472
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3473
|
+
}
|
|
3474
|
+
if (current.status !== "running") {
|
|
3475
|
+
throw new ReducerError("illegal-transition", `succeed ${current.status}`);
|
|
3476
|
+
}
|
|
3477
|
+
if (current.operationID !== event.operationID || current.attemptID !== event.attemptID) {
|
|
3478
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3479
|
+
}
|
|
3480
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, {
|
|
3481
|
+
status: "succeeded",
|
|
3482
|
+
attempts: current.attempts,
|
|
3483
|
+
operationID: event.operationID,
|
|
3484
|
+
attemptID: event.attemptID,
|
|
3485
|
+
artifactSha256: event.artifact.sha256
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3488
|
+
function applyFailed(state, event) {
|
|
3489
|
+
const current = state.nodes[event.nodeID];
|
|
3490
|
+
if (current === undefined) {
|
|
3491
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3492
|
+
}
|
|
3493
|
+
if (current.status === "failed") {
|
|
3494
|
+
if (current.operationID === event.operationID && current.attemptID === event.attemptID) {
|
|
3495
|
+
return state;
|
|
3496
|
+
}
|
|
3497
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3498
|
+
}
|
|
3499
|
+
if (current.status !== "running") {
|
|
3500
|
+
throw new ReducerError("illegal-transition", `fail ${current.status}`);
|
|
3501
|
+
}
|
|
3502
|
+
if (current.operationID !== event.operationID || current.attemptID !== event.attemptID) {
|
|
3503
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3504
|
+
}
|
|
3505
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, {
|
|
3506
|
+
status: "failed",
|
|
3507
|
+
attempts: current.attempts,
|
|
3508
|
+
operationID: event.operationID,
|
|
3509
|
+
attemptID: event.attemptID
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
function applyCancelled(state, event) {
|
|
3513
|
+
const current = state.nodes[event.nodeID];
|
|
3514
|
+
if (current === undefined) {
|
|
3515
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3516
|
+
}
|
|
3517
|
+
if (current.status === "cancelled") {
|
|
3518
|
+
return state;
|
|
3519
|
+
}
|
|
3520
|
+
if (current.status === "succeeded") {
|
|
3521
|
+
throw new ReducerError("illegal-transition", "cancel succeeded");
|
|
3522
|
+
}
|
|
3523
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, {
|
|
3524
|
+
...current,
|
|
3525
|
+
status: "cancelled"
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
function applyRunCompleted(state, seq) {
|
|
3529
|
+
for (const node of Object.values(state.nodes)) {
|
|
3530
|
+
if (node.status !== "succeeded" && node.status !== "cancelled") {
|
|
3531
|
+
throw new ReducerError("incomplete-run", node.status);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
return { ...state, status: "completed", seq };
|
|
3535
|
+
}
|
|
3536
|
+
function applyEvent(spec, state, event) {
|
|
3537
|
+
if (event.runID !== state.runID) {
|
|
3538
|
+
throw new ReducerError("run-mismatch", event.runID);
|
|
3539
|
+
}
|
|
3540
|
+
if (TERMINAL_RUN_STATUSES.has(state.status)) {
|
|
3541
|
+
if (event.type === "run.completed" && state.status === "completed" || event.type === "run.failed" && state.status === "failed" || event.type === "run.cancelled" && state.status === "cancelled") {
|
|
3542
|
+
return state;
|
|
3543
|
+
}
|
|
3544
|
+
throw new ReducerError("post-terminal", event.type);
|
|
3545
|
+
}
|
|
3546
|
+
switch (event.type) {
|
|
3547
|
+
case "run.started":
|
|
3548
|
+
return applyRunStarted(state, event.seq, event.specDigest);
|
|
3549
|
+
case "node.dispatched":
|
|
3550
|
+
return applyDispatched(spec, state, event);
|
|
3551
|
+
case "node.succeeded":
|
|
3552
|
+
return applySucceeded(state, event);
|
|
3553
|
+
case "node.failed":
|
|
3554
|
+
return applyFailed(state, event);
|
|
3555
|
+
case "node.cancelled":
|
|
3556
|
+
return applyCancelled(state, event);
|
|
3557
|
+
case "run.completed":
|
|
3558
|
+
return applyRunCompleted(state, event.seq);
|
|
3559
|
+
case "run.failed":
|
|
3560
|
+
return { ...state, status: "failed", seq: event.seq };
|
|
3561
|
+
case "run.cancelled":
|
|
3562
|
+
return { ...state, status: "cancelled", seq: event.seq };
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
// src/graph/replay.ts
|
|
3567
|
+
class ReplayError extends Error {
|
|
3568
|
+
code;
|
|
3569
|
+
detail;
|
|
3570
|
+
constructor(detail) {
|
|
3571
|
+
super(`sequence-gap: ${detail}`);
|
|
3572
|
+
this.name = "ReplayError";
|
|
3573
|
+
this.code = "sequence-gap";
|
|
3574
|
+
this.detail = detail;
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
function replay(spec, events) {
|
|
3578
|
+
let state = initialState(spec);
|
|
3579
|
+
events.forEach((event, index) => {
|
|
3580
|
+
if (event.seq !== index) {
|
|
3581
|
+
throw new ReplayError(`expected ${index}, got ${event.seq}`);
|
|
3582
|
+
}
|
|
3583
|
+
state = applyEvent(spec, state, event);
|
|
3584
|
+
});
|
|
3585
|
+
return state;
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
// src/graph/schema.ts
|
|
3589
|
+
import { z as z7 } from "zod";
|
|
3590
|
+
|
|
3591
|
+
// src/context/schema.ts
|
|
3592
|
+
import { z as z6 } from "zod";
|
|
3593
|
+
var PrivacyClassSchema = z6.enum(["public", "internal", "sensitive"]);
|
|
3594
|
+
var Sha256Schema = z6.string().regex(/^[0-9a-f]{64}$/, "expected lowercase hex sha-256");
|
|
3595
|
+
var IdentifierSchema = z6.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "invalid identifier");
|
|
3596
|
+
var TaskAnchorSchema = z6.object({
|
|
3597
|
+
taskID: IdentifierSchema,
|
|
3598
|
+
specDigest: Sha256Schema
|
|
3599
|
+
}).strict();
|
|
3600
|
+
var ContextRefSchema = z6.object({
|
|
3601
|
+
uri: z6.string().min(1).max(1024),
|
|
3602
|
+
sha256: Sha256Schema,
|
|
3603
|
+
bytes: z6.number().int().nonnegative(),
|
|
3604
|
+
mediaType: z6.string().min(1).max(128).optional()
|
|
3605
|
+
}).strict();
|
|
3606
|
+
var ManifestEntrySchema = z6.object({
|
|
3607
|
+
label: IdentifierSchema,
|
|
3608
|
+
sha256: Sha256Schema,
|
|
3609
|
+
bytes: z6.number().int().nonnegative(),
|
|
3610
|
+
ref: ContextRefSchema.optional()
|
|
3611
|
+
}).strict();
|
|
3612
|
+
var ArtifactManifestSchema = z6.object({
|
|
3613
|
+
version: z6.literal(1),
|
|
3614
|
+
taskID: IdentifierSchema,
|
|
3615
|
+
privacyClass: PrivacyClassSchema,
|
|
3616
|
+
entries: z6.array(ManifestEntrySchema)
|
|
3617
|
+
}).strict();
|
|
3618
|
+
var NodeContextContractSchema = z6.object({
|
|
3619
|
+
version: z6.literal(1),
|
|
3620
|
+
refs: z6.array(ContextRefSchema),
|
|
3621
|
+
privacyClass: PrivacyClassSchema,
|
|
3622
|
+
maxInputTokens: z6.number().int().positive().optional()
|
|
3623
|
+
}).strict();
|
|
3624
|
+
|
|
3625
|
+
// src/graph/schema.ts
|
|
3626
|
+
var GRAPH_SPEC_VERSION = 1;
|
|
3627
|
+
var GRAPH_EVENT_VERSION = 1;
|
|
3628
|
+
var NodeIDSchema = z7.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "invalid node id");
|
|
3629
|
+
var NodeRoleSchema = z7.enum(["implementer", "reviewer"]);
|
|
3630
|
+
var OperationIDSchema = z7.string().regex(/^[0-9a-f]{8,64}$/, "invalid operation id");
|
|
3631
|
+
var AttemptIDSchema = z7.string().regex(/^[0-9a-f]{8,64}$/, "invalid attempt id");
|
|
3632
|
+
var ErrorClassSchema = z7.enum([
|
|
3633
|
+
"transient",
|
|
3634
|
+
"permanent",
|
|
3635
|
+
"timeout",
|
|
3636
|
+
"cancelled",
|
|
3637
|
+
"unknown"
|
|
3638
|
+
]);
|
|
3639
|
+
var GraphNodeSchema = z7.object({
|
|
3640
|
+
id: NodeIDSchema,
|
|
3641
|
+
role: NodeRoleSchema,
|
|
3642
|
+
dependsOn: z7.array(NodeIDSchema).default([]),
|
|
3643
|
+
anchor: TaskAnchorSchema,
|
|
3644
|
+
reviews: NodeIDSchema.optional()
|
|
3645
|
+
}).strict();
|
|
3646
|
+
var GraphSpecV1Schema = z7.object({
|
|
3647
|
+
version: z7.literal(GRAPH_SPEC_VERSION),
|
|
3648
|
+
runID: NodeIDSchema,
|
|
3649
|
+
nodes: z7.array(GraphNodeSchema).min(1)
|
|
3650
|
+
}).strict();
|
|
3651
|
+
var eventEnvelope = {
|
|
3652
|
+
v: z7.literal(GRAPH_EVENT_VERSION),
|
|
3653
|
+
seq: z7.number().int().nonnegative(),
|
|
3654
|
+
runID: NodeIDSchema
|
|
3655
|
+
};
|
|
3656
|
+
var RunStartedEventSchema = z7.object({
|
|
3657
|
+
...eventEnvelope,
|
|
3658
|
+
type: z7.literal("run.started"),
|
|
3659
|
+
specDigest: Sha256Schema
|
|
3660
|
+
}).strict();
|
|
3661
|
+
var NodeDispatchedEventSchema = z7.object({
|
|
3662
|
+
...eventEnvelope,
|
|
3663
|
+
type: z7.literal("node.dispatched"),
|
|
3664
|
+
nodeID: NodeIDSchema,
|
|
3665
|
+
operationID: OperationIDSchema,
|
|
3666
|
+
attemptID: AttemptIDSchema
|
|
3667
|
+
}).strict();
|
|
3668
|
+
var NodeSucceededEventSchema = z7.object({
|
|
3669
|
+
...eventEnvelope,
|
|
3670
|
+
type: z7.literal("node.succeeded"),
|
|
3671
|
+
nodeID: NodeIDSchema,
|
|
3672
|
+
operationID: OperationIDSchema,
|
|
3673
|
+
attemptID: AttemptIDSchema,
|
|
3674
|
+
artifact: ContextRefSchema
|
|
3675
|
+
}).strict();
|
|
3676
|
+
var NodeFailedEventSchema = z7.object({
|
|
3677
|
+
...eventEnvelope,
|
|
3678
|
+
type: z7.literal("node.failed"),
|
|
3679
|
+
nodeID: NodeIDSchema,
|
|
3680
|
+
operationID: OperationIDSchema,
|
|
3681
|
+
attemptID: AttemptIDSchema,
|
|
3682
|
+
errorClass: ErrorClassSchema
|
|
3683
|
+
}).strict();
|
|
3684
|
+
var NodeCancelledEventSchema = z7.object({
|
|
3685
|
+
...eventEnvelope,
|
|
3686
|
+
type: z7.literal("node.cancelled"),
|
|
3687
|
+
nodeID: NodeIDSchema
|
|
3688
|
+
}).strict();
|
|
3689
|
+
var RunCompletedEventSchema = z7.object({ ...eventEnvelope, type: z7.literal("run.completed") }).strict();
|
|
3690
|
+
var RunFailedEventSchema = z7.object({ ...eventEnvelope, type: z7.literal("run.failed") }).strict();
|
|
3691
|
+
var RunCancelledEventSchema = z7.object({ ...eventEnvelope, type: z7.literal("run.cancelled") }).strict();
|
|
3692
|
+
var GraphEventV1Schema = z7.discriminatedUnion("type", [
|
|
3693
|
+
RunStartedEventSchema,
|
|
3694
|
+
NodeDispatchedEventSchema,
|
|
3695
|
+
NodeSucceededEventSchema,
|
|
3696
|
+
NodeFailedEventSchema,
|
|
3697
|
+
NodeCancelledEventSchema,
|
|
3698
|
+
RunCompletedEventSchema,
|
|
3699
|
+
RunFailedEventSchema,
|
|
3700
|
+
RunCancelledEventSchema
|
|
3701
|
+
]);
|
|
3702
|
+
|
|
3703
|
+
class GraphSchemaError extends Error {
|
|
3704
|
+
code;
|
|
3705
|
+
issues;
|
|
3706
|
+
constructor(code, error) {
|
|
3707
|
+
super(`${code}: ${error.issues.map((issue) => issue.message).join("; ")}`);
|
|
3708
|
+
this.name = "GraphSchemaError";
|
|
3709
|
+
this.code = code;
|
|
3710
|
+
this.issues = error.issues.map((issue) => issue.message);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
function parseGraphSpec(input) {
|
|
3714
|
+
const result = GraphSpecV1Schema.safeParse(input);
|
|
3715
|
+
if (!result.success) {
|
|
3716
|
+
throw new GraphSchemaError("invalid-spec", result.error);
|
|
3717
|
+
}
|
|
3718
|
+
return result.data;
|
|
3719
|
+
}
|
|
3720
|
+
function parseGraphEvent(input) {
|
|
3721
|
+
const result = GraphEventV1Schema.safeParse(input);
|
|
3722
|
+
if (!result.success) {
|
|
3723
|
+
throw new GraphSchemaError("invalid-event", result.error);
|
|
3724
|
+
}
|
|
3725
|
+
return result.data;
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
// src/storage/graph/codec.ts
|
|
3729
|
+
var CODEC_VERSION = 1;
|
|
3730
|
+
var GENESIS_DIGEST = sha256Hex("openteam/graph-journal/genesis/v1");
|
|
3731
|
+
var NUL = "\x00";
|
|
3732
|
+
var NEWLINE = `
|
|
3733
|
+
`;
|
|
3734
|
+
function canonical(value) {
|
|
3735
|
+
if (value === null || typeof value !== "object") {
|
|
3736
|
+
return JSON.stringify(value);
|
|
3737
|
+
}
|
|
3738
|
+
const record = value;
|
|
3739
|
+
const entries = Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonical(record[key])}`);
|
|
3740
|
+
return `{${entries.join(",")}}`;
|
|
3741
|
+
}
|
|
3742
|
+
function frameDigest(prev, event) {
|
|
3743
|
+
return sha256Hex(`${prev}${NUL}${canonical(event)}`);
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
class JournalCodecError extends Error {
|
|
3747
|
+
code;
|
|
3748
|
+
detail;
|
|
3749
|
+
constructor(code, detail) {
|
|
3750
|
+
super(`${code}: ${detail}`);
|
|
3751
|
+
this.name = "JournalCodecError";
|
|
3752
|
+
this.code = code;
|
|
3753
|
+
this.detail = detail;
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
function encodeFrame(prev, seq, event) {
|
|
3757
|
+
const sum = frameDigest(prev, event);
|
|
3758
|
+
const line = canonical({ v: CODEC_VERSION, seq, prev, sum, event });
|
|
3759
|
+
return { line, digest: sum };
|
|
3760
|
+
}
|
|
3761
|
+
function decodeFrame(raw, index, expectedPrev) {
|
|
3762
|
+
let parsed;
|
|
3763
|
+
try {
|
|
3764
|
+
parsed = JSON.parse(raw);
|
|
3765
|
+
} catch {
|
|
3766
|
+
throw new JournalCodecError("bad-frame", `unparseable frame ${index}`);
|
|
3767
|
+
}
|
|
3768
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3769
|
+
throw new JournalCodecError("bad-frame", `frame ${index} is not an object`);
|
|
3770
|
+
}
|
|
3771
|
+
const frame = parsed;
|
|
3772
|
+
if (frame.v !== CODEC_VERSION) {
|
|
3773
|
+
throw new JournalCodecError("bad-version", `frame ${index}`);
|
|
3774
|
+
}
|
|
3775
|
+
if (typeof frame.seq !== "number" || typeof frame.prev !== "string" || typeof frame.sum !== "string") {
|
|
3776
|
+
throw new JournalCodecError("bad-frame", `frame ${index} header`);
|
|
3777
|
+
}
|
|
3778
|
+
let event;
|
|
3779
|
+
try {
|
|
3780
|
+
event = parseGraphEvent(frame.event);
|
|
3781
|
+
} catch {
|
|
3782
|
+
throw new JournalCodecError("bad-frame", `frame ${index} event`);
|
|
3783
|
+
}
|
|
3784
|
+
if (frame.seq !== index) {
|
|
3785
|
+
throw new JournalCodecError("bad-sequence", `expected ${index}, got ${frame.seq}`);
|
|
3786
|
+
}
|
|
3787
|
+
if (frame.prev !== expectedPrev) {
|
|
3788
|
+
throw new JournalCodecError("chain-break", `frame ${index}`);
|
|
3789
|
+
}
|
|
3790
|
+
if (frame.sum !== frameDigest(frame.prev, event)) {
|
|
3791
|
+
throw new JournalCodecError("bad-checksum", `frame ${index}`);
|
|
3792
|
+
}
|
|
3793
|
+
return { event, digest: frame.sum };
|
|
3794
|
+
}
|
|
3795
|
+
function decodeJournal(text) {
|
|
3796
|
+
const segments = text.split(NEWLINE);
|
|
3797
|
+
const trailing = segments.pop() ?? "";
|
|
3798
|
+
const partialTail = trailing !== "";
|
|
3799
|
+
const events = [];
|
|
3800
|
+
let prev = GENESIS_DIGEST;
|
|
3801
|
+
segments.forEach((raw, index) => {
|
|
3802
|
+
const { event, digest } = decodeFrame(raw, index, prev);
|
|
3803
|
+
events.push(event);
|
|
3804
|
+
prev = digest;
|
|
3805
|
+
});
|
|
3806
|
+
return { events, digest: prev, partialTail };
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
// src/storage/graph/provider.ts
|
|
3810
|
+
class JournalError extends Error {
|
|
3811
|
+
code;
|
|
3812
|
+
detail;
|
|
3813
|
+
constructor(code, detail) {
|
|
3814
|
+
super(`${code}: ${detail}`);
|
|
3815
|
+
this.name = "JournalError";
|
|
3816
|
+
this.code = code;
|
|
3817
|
+
this.detail = detail;
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
// src/storage/graph/writer.ts
|
|
3822
|
+
import {
|
|
3823
|
+
closeSync,
|
|
3824
|
+
existsSync,
|
|
3825
|
+
fsyncSync,
|
|
3826
|
+
mkdirSync,
|
|
3827
|
+
openSync,
|
|
3828
|
+
readFileSync,
|
|
3829
|
+
realpathSync,
|
|
3830
|
+
writeSync
|
|
3831
|
+
} from "node:fs";
|
|
3832
|
+
function ensureDir(dir) {
|
|
3833
|
+
mkdirSync(dir, { recursive: true });
|
|
3834
|
+
}
|
|
3835
|
+
function writeDurable(file, content) {
|
|
3836
|
+
const fd = openSync(file, "w");
|
|
3837
|
+
try {
|
|
3838
|
+
writeSync(fd, content);
|
|
3839
|
+
fsyncSync(fd);
|
|
3840
|
+
} finally {
|
|
3841
|
+
closeSync(fd);
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
function appendDurable(file, content) {
|
|
3845
|
+
const fd = openSync(file, "a");
|
|
3846
|
+
try {
|
|
3847
|
+
writeSync(fd, content);
|
|
3848
|
+
fsyncSync(fd);
|
|
3849
|
+
} finally {
|
|
3850
|
+
closeSync(fd);
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
function readText(file) {
|
|
3854
|
+
if (!existsSync(file)) {
|
|
3855
|
+
return;
|
|
3856
|
+
}
|
|
3857
|
+
return readFileSync(file, "utf8");
|
|
3858
|
+
}
|
|
3859
|
+
function pathExists(path) {
|
|
3860
|
+
return existsSync(path);
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3863
|
+
// src/storage/graph/fsGraphJournal.ts
|
|
3864
|
+
var NUL2 = "\x00";
|
|
3865
|
+
var JOURNAL_FILE = "journal.ndjson";
|
|
3866
|
+
var OWNER_FILE = "owner";
|
|
3867
|
+
var SPEC_FILE = "spec.json";
|
|
3868
|
+
function createFsGraphJournal(root) {
|
|
3869
|
+
let counter = 0;
|
|
3870
|
+
const runDir = (runID) => join2(root, runID);
|
|
3871
|
+
const journalPath = (runID) => join2(runDir(runID), JOURNAL_FILE);
|
|
3872
|
+
const ownerPath = (runID) => join2(runDir(runID), OWNER_FILE);
|
|
3873
|
+
const specPath = (runID) => join2(runDir(runID), SPEC_FILE);
|
|
3874
|
+
const issueToken = (runID, prev) => {
|
|
3875
|
+
const token = sha256Hex(`writer${NUL2}${runID}${NUL2}${prev}${NUL2}${counter}`).slice(0, 32);
|
|
3876
|
+
counter += 1;
|
|
3877
|
+
return token;
|
|
3878
|
+
};
|
|
3879
|
+
const readDecoded = (runID) => {
|
|
3880
|
+
const text = readText(journalPath(runID)) ?? "";
|
|
3881
|
+
try {
|
|
3882
|
+
return decodeJournal(text);
|
|
3883
|
+
} catch (error) {
|
|
3884
|
+
throw new JournalError("corrupt", error.message);
|
|
3885
|
+
}
|
|
3886
|
+
};
|
|
3887
|
+
return {
|
|
3888
|
+
async create(spec) {
|
|
3889
|
+
const parsed = parseGraphSpec(structuredClone(spec));
|
|
3890
|
+
if (pathExists(runDir(parsed.runID))) {
|
|
3891
|
+
throw new JournalError("already-exists", parsed.runID);
|
|
3892
|
+
}
|
|
3893
|
+
ensureDir(runDir(parsed.runID));
|
|
3894
|
+
writeDurable(specPath(parsed.runID), JSON.stringify(parsed));
|
|
3895
|
+
writeDurable(journalPath(parsed.runID), "");
|
|
3896
|
+
const token = issueToken(parsed.runID, GENESIS_DIGEST);
|
|
3897
|
+
writeDurable(ownerPath(parsed.runID), token);
|
|
3898
|
+
return { runID: parsed.runID, writerToken: token };
|
|
3899
|
+
},
|
|
3900
|
+
async takeOver(runID) {
|
|
3901
|
+
if (!pathExists(runDir(runID))) {
|
|
3902
|
+
throw new JournalError("not-found", runID);
|
|
3903
|
+
}
|
|
3904
|
+
const prev = readText(ownerPath(runID)) ?? GENESIS_DIGEST;
|
|
3905
|
+
const token = issueToken(runID, prev);
|
|
3906
|
+
writeDurable(ownerPath(runID), token);
|
|
3907
|
+
return { runID, writerToken: token };
|
|
3908
|
+
},
|
|
3909
|
+
async append(handle, event, expectedSeq) {
|
|
3910
|
+
if (!pathExists(runDir(handle.runID))) {
|
|
3911
|
+
throw new JournalError("not-found", handle.runID);
|
|
3912
|
+
}
|
|
3913
|
+
if (readText(ownerPath(handle.runID)) !== handle.writerToken) {
|
|
3914
|
+
throw new JournalError("writer-conflict", handle.runID);
|
|
3915
|
+
}
|
|
3916
|
+
const decoded = readDecoded(handle.runID);
|
|
3917
|
+
if (decoded.partialTail) {
|
|
3918
|
+
throw new JournalError("corrupt", "partial tail; recovery required");
|
|
3919
|
+
}
|
|
3920
|
+
const head = decoded.events.length;
|
|
3921
|
+
if (event.seq < head) {
|
|
3922
|
+
throw new JournalError("duplicate-event", `seq ${event.seq}`);
|
|
3923
|
+
}
|
|
3924
|
+
if (event.seq !== head || expectedSeq !== head) {
|
|
3925
|
+
throw new JournalError("sequence-mismatch", `head=${head} expected=${expectedSeq} event=${event.seq}`);
|
|
3926
|
+
}
|
|
3927
|
+
const frame = encodeFrame(decoded.digest, head, event);
|
|
3928
|
+
appendDurable(journalPath(handle.runID), `${frame.line}
|
|
3929
|
+
`);
|
|
3930
|
+
},
|
|
3931
|
+
async load(runID) {
|
|
3932
|
+
if (!pathExists(runDir(runID))) {
|
|
3933
|
+
throw new JournalError("not-found", runID);
|
|
3934
|
+
}
|
|
3935
|
+
const decoded = readDecoded(runID);
|
|
3936
|
+
const specText = readText(specPath(runID));
|
|
3937
|
+
if (specText === undefined) {
|
|
3938
|
+
throw new JournalError("corrupt", "missing spec");
|
|
3939
|
+
}
|
|
3940
|
+
let spec;
|
|
3941
|
+
try {
|
|
3942
|
+
spec = parseGraphSpec(JSON.parse(specText));
|
|
3943
|
+
} catch (error) {
|
|
3944
|
+
throw new JournalError("corrupt", error.message);
|
|
3945
|
+
}
|
|
3946
|
+
let loaded;
|
|
3947
|
+
try {
|
|
3948
|
+
loaded = {
|
|
3949
|
+
spec,
|
|
3950
|
+
events: decoded.events,
|
|
3951
|
+
state: replay(spec, decoded.events)
|
|
3952
|
+
};
|
|
3953
|
+
} catch (error) {
|
|
3954
|
+
throw new JournalError("corrupt", error.message);
|
|
3955
|
+
}
|
|
3956
|
+
return loaded;
|
|
3957
|
+
},
|
|
3958
|
+
async exists(runID) {
|
|
3959
|
+
return pathExists(runDir(runID));
|
|
3960
|
+
}
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
// src/telemetry/graphProjection.ts
|
|
3965
|
+
function projectGraphEvent(event) {
|
|
3966
|
+
const record = {
|
|
3967
|
+
v: event.v,
|
|
3968
|
+
seq: event.seq,
|
|
3969
|
+
runID: event.runID,
|
|
3970
|
+
type: event.type,
|
|
3971
|
+
..."nodeID" in event ? { nodeID: event.nodeID } : {},
|
|
3972
|
+
..."operationID" in event ? { operationID: event.operationID } : {},
|
|
3973
|
+
..."attemptID" in event ? { attemptID: event.attemptID } : {},
|
|
3974
|
+
..."errorClass" in event ? { errorClass: event.errorClass } : {},
|
|
3975
|
+
..."specDigest" in event ? { specDigest: event.specDigest } : {},
|
|
3976
|
+
..."artifact" in event ? { artifact: redactedRef(event.artifact) } : {}
|
|
3977
|
+
};
|
|
3978
|
+
assertReferenceOnly(record);
|
|
3979
|
+
return record;
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3982
|
+
// src/telemetry/graphRuntimeHealth.ts
|
|
3983
|
+
var RUN_TERMINAL_TYPES = new Set([
|
|
3984
|
+
"run.completed",
|
|
3985
|
+
"run.failed",
|
|
3986
|
+
"run.cancelled"
|
|
3987
|
+
]);
|
|
3988
|
+
var NODE_TERMINAL_TYPES = new Set([
|
|
3989
|
+
"node.succeeded",
|
|
3990
|
+
"node.failed",
|
|
3991
|
+
"node.cancelled"
|
|
3992
|
+
]);
|
|
3993
|
+
function isRecoveryRequired(journal, unknownEffects) {
|
|
3994
|
+
return journal === "corrupt" || unknownEffects > 0;
|
|
3995
|
+
}
|
|
3996
|
+
function computeGraphRuntimeHealth(input) {
|
|
3997
|
+
const dispatched = new Set;
|
|
3998
|
+
const resolved = new Set;
|
|
3999
|
+
let runTerminalSeq = Number.POSITIVE_INFINITY;
|
|
4000
|
+
const staleAttempts = new Set;
|
|
4001
|
+
for (const event of input.events) {
|
|
4002
|
+
if (event.type === "node.dispatched") {
|
|
4003
|
+
dispatched.add(event.attemptID);
|
|
4004
|
+
} else if (NODE_TERMINAL_TYPES.has(event.type)) {
|
|
4005
|
+
if ("attemptID" in event && event.attemptID !== undefined) {
|
|
4006
|
+
resolved.add(event.attemptID);
|
|
4007
|
+
if (event.seq > runTerminalSeq) {
|
|
4008
|
+
staleAttempts.add(event.attemptID);
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
if (RUN_TERMINAL_TYPES.has(event.type)) {
|
|
4013
|
+
if (event.seq < runTerminalSeq) {
|
|
4014
|
+
runTerminalSeq = event.seq;
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
let unknownEffects = 0;
|
|
4019
|
+
for (const attemptID of dispatched) {
|
|
4020
|
+
if (!resolved.has(attemptID)) {
|
|
4021
|
+
unknownEffects++;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
const staleCompletions = staleAttempts.size;
|
|
4025
|
+
const recoveryRequired = isRecoveryRequired(input.journal, unknownEffects);
|
|
4026
|
+
let conflicts = 0;
|
|
4027
|
+
if (input.lease !== undefined && !input.lease.released && input.lease.holder !== input.holder) {
|
|
4028
|
+
conflicts++;
|
|
4029
|
+
}
|
|
4030
|
+
if (input.worktrees !== undefined) {
|
|
4031
|
+
conflicts += input.worktrees.blocked;
|
|
4032
|
+
}
|
|
4033
|
+
const health = {
|
|
4034
|
+
recoveryRequired,
|
|
4035
|
+
unknownEffects,
|
|
4036
|
+
staleCompletions,
|
|
4037
|
+
conflicts,
|
|
4038
|
+
killSwitch: input.killSwitch
|
|
4039
|
+
};
|
|
4040
|
+
assertReferenceOnly(health);
|
|
4041
|
+
return health;
|
|
4042
|
+
}
|
|
4043
|
+
|
|
4044
|
+
// src/telemetry/graphView.ts
|
|
4045
|
+
function isReady(node, state) {
|
|
4046
|
+
return node.dependsOn.every((dep) => state.nodes[dep]?.status === "succeeded");
|
|
4047
|
+
}
|
|
4048
|
+
function toViewState(node, status, state) {
|
|
4049
|
+
if (status === "running") {
|
|
4050
|
+
return "active";
|
|
4051
|
+
}
|
|
4052
|
+
if (status === "pending") {
|
|
4053
|
+
return isReady(node, state) ? "ready" : "pending";
|
|
4054
|
+
}
|
|
4055
|
+
return status;
|
|
4056
|
+
}
|
|
4057
|
+
function deriveVerdict(status) {
|
|
4058
|
+
if (status === "succeeded") {
|
|
4059
|
+
return "approved";
|
|
4060
|
+
}
|
|
4061
|
+
if (status === "failed") {
|
|
4062
|
+
return "rejected";
|
|
4063
|
+
}
|
|
4064
|
+
return "inconclusive";
|
|
4065
|
+
}
|
|
4066
|
+
function indexTelemetry(telemetry) {
|
|
4067
|
+
const lastError = new Map;
|
|
4068
|
+
const lastArtifact = new Map;
|
|
4069
|
+
for (const record of telemetry) {
|
|
4070
|
+
if (record.nodeID === undefined) {
|
|
4071
|
+
continue;
|
|
4072
|
+
}
|
|
4073
|
+
if (record.type === "node.failed" && record.errorClass !== undefined) {
|
|
4074
|
+
lastError.set(record.nodeID, record.errorClass);
|
|
4075
|
+
}
|
|
4076
|
+
if (record.type === "node.succeeded" && record.artifact !== undefined) {
|
|
4077
|
+
lastArtifact.set(record.nodeID, record.artifact);
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
return { lastError, lastArtifact };
|
|
4081
|
+
}
|
|
4082
|
+
function buildNodeView(node, input, lastError, lastArtifact) {
|
|
4083
|
+
const runtime = input.state.nodes[node.id];
|
|
4084
|
+
const status = runtime?.status ?? "pending";
|
|
4085
|
+
const attempts = runtime?.attempts ?? 0;
|
|
4086
|
+
const errorClass = lastError.get(node.id);
|
|
4087
|
+
const artifact = lastArtifact.get(node.id);
|
|
4088
|
+
const sessionRef = input.sessions?.[node.id];
|
|
4089
|
+
const review = node.role === "reviewer" && node.reviews !== undefined ? { verdict: deriveVerdict(status), reassigned: attempts > 1 } : undefined;
|
|
4090
|
+
return {
|
|
4091
|
+
nodeID: node.id,
|
|
4092
|
+
role: node.role,
|
|
4093
|
+
state: toViewState(node, status, input.state),
|
|
4094
|
+
attempts,
|
|
4095
|
+
...errorClass !== undefined ? { lastErrorClass: errorClass } : {},
|
|
4096
|
+
...review !== undefined ? { review } : {},
|
|
4097
|
+
...node.reviews !== undefined ? { revisionOf: node.reviews } : {},
|
|
4098
|
+
...artifact !== undefined ? { artifact } : {},
|
|
4099
|
+
...sessionRef !== undefined ? { sessionRef } : {}
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
function buildEdges(spec) {
|
|
4103
|
+
return spec.nodes.flatMap((node) => node.dependsOn.map((from) => ({ from, to: node.id })));
|
|
4104
|
+
}
|
|
4105
|
+
function buildGraphView(input) {
|
|
4106
|
+
const { lastError, lastArtifact } = indexTelemetry(input.telemetry);
|
|
4107
|
+
const nodes = input.spec.nodes.map((node) => buildNodeView(node, input, lastError, lastArtifact));
|
|
4108
|
+
const projection = {
|
|
4109
|
+
version: 1,
|
|
4110
|
+
runID: input.spec.runID,
|
|
4111
|
+
nodes,
|
|
4112
|
+
edges: buildEdges(input.spec),
|
|
4113
|
+
health: input.health
|
|
4114
|
+
};
|
|
4115
|
+
assertReferenceOnly(projection);
|
|
4116
|
+
return projection;
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
// src/cli/graphViewProvider.ts
|
|
4120
|
+
function selectMostRecentRun(journalRoot) {
|
|
4121
|
+
if (!pathExists(journalRoot)) {
|
|
4122
|
+
return;
|
|
4123
|
+
}
|
|
4124
|
+
let entries;
|
|
4125
|
+
try {
|
|
4126
|
+
entries = readdirSync(journalRoot);
|
|
4127
|
+
} catch {
|
|
4128
|
+
return;
|
|
4129
|
+
}
|
|
4130
|
+
if (entries.length === 0) {
|
|
4131
|
+
return;
|
|
4132
|
+
}
|
|
4133
|
+
let best;
|
|
4134
|
+
for (const name of entries) {
|
|
4135
|
+
const specFile = join3(journalRoot, name, "spec.json");
|
|
4136
|
+
let mtime;
|
|
4137
|
+
try {
|
|
4138
|
+
mtime = statSync(specFile).mtimeMs;
|
|
4139
|
+
} catch {
|
|
4140
|
+
continue;
|
|
4141
|
+
}
|
|
4142
|
+
if (best === undefined || mtime > best.mtime || mtime === best.mtime && name > best.runID) {
|
|
4143
|
+
best = { runID: name, mtime };
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
return best?.runID;
|
|
4147
|
+
}
|
|
4148
|
+
function createGraphViewProvider(deps) {
|
|
4149
|
+
const selectRun = deps.selectRun ?? selectMostRecentRun;
|
|
4150
|
+
const journal = createFsGraphJournal(deps.journalRoot);
|
|
4151
|
+
return {
|
|
4152
|
+
async readView() {
|
|
4153
|
+
const runID = selectRun(deps.journalRoot);
|
|
4154
|
+
if (runID === undefined) {
|
|
4155
|
+
return null;
|
|
4156
|
+
}
|
|
4157
|
+
try {
|
|
4158
|
+
const loaded = await journal.load(runID);
|
|
4159
|
+
const telemetry = loaded.events.map(projectGraphEvent);
|
|
4160
|
+
const health = computeGraphRuntimeHealth({
|
|
4161
|
+
events: loaded.events,
|
|
4162
|
+
journal: "clean",
|
|
4163
|
+
lease: undefined,
|
|
4164
|
+
holder: "console",
|
|
4165
|
+
killSwitch: deps.killSwitch
|
|
4166
|
+
});
|
|
4167
|
+
return buildGraphView({
|
|
4168
|
+
spec: loaded.spec,
|
|
4169
|
+
state: loaded.state,
|
|
4170
|
+
telemetry,
|
|
4171
|
+
health
|
|
4172
|
+
});
|
|
4173
|
+
} catch (error) {
|
|
4174
|
+
if (error instanceof JournalError && error.code === "corrupt") {
|
|
4175
|
+
deps.log(`Graph view: journal for run "${runID}" is corrupt (${error.detail}); skipping.`);
|
|
4176
|
+
return null;
|
|
4177
|
+
}
|
|
4178
|
+
if (error instanceof JournalError && error.code === "not-found") {
|
|
4179
|
+
return null;
|
|
4180
|
+
}
|
|
4181
|
+
throw error;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
};
|
|
4185
|
+
}
|
|
4186
|
+
|
|
3304
4187
|
// src/cli/consoleServe.ts
|
|
3305
4188
|
function parseConsoleServeArgs(rest) {
|
|
3306
4189
|
return {
|
|
@@ -3323,6 +4206,14 @@ async function runConsoleServe(deps, options) {
|
|
|
3323
4206
|
endpoint,
|
|
3324
4207
|
...deps.serverPassword !== undefined ? { password: deps.serverPassword } : {}
|
|
3325
4208
|
}));
|
|
4209
|
+
const graphMode = config.graph?.mode ?? "off";
|
|
4210
|
+
const graphViewEnabled = config.console.graphView?.enabled === true;
|
|
4211
|
+
const buildGraphView2 = deps.createGraphViewProvider ?? createGraphViewProvider;
|
|
4212
|
+
const graphViewProvider = graphMode !== "off" && graphViewEnabled ? buildGraphView2({
|
|
4213
|
+
journalRoot: config.graph?.journalRoot ?? DEFAULT_GRAPH_JOURNAL_ROOT,
|
|
4214
|
+
killSwitch: config.graph?.killSwitch ?? false,
|
|
4215
|
+
log: deps.log
|
|
4216
|
+
}) : undefined;
|
|
3326
4217
|
let runtime;
|
|
3327
4218
|
try {
|
|
3328
4219
|
runtime = await start({
|
|
@@ -3343,6 +4234,7 @@ async function runConsoleServe(deps, options) {
|
|
|
3343
4234
|
createClient: makeClient,
|
|
3344
4235
|
createPtyClient: makePtyClient
|
|
3345
4236
|
},
|
|
4237
|
+
...graphViewProvider !== undefined ? { graphView: graphViewProvider } : {},
|
|
3346
4238
|
...config.console.remoteStorage ? {
|
|
3347
4239
|
storageApi: {
|
|
3348
4240
|
storage: deps.storage,
|
|
@@ -4535,8 +5427,11 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
4535
5427
|
"con `mode: subagent` (usa `read`/`glob`/`list`).",
|
|
4536
5428
|
"",
|
|
4537
5429
|
"- **Si el equipo YA existe** → actúa como coordinador: entiende la tarea,",
|
|
4538
|
-
"
|
|
4539
|
-
"
|
|
5430
|
+
" identifica qué roles del roster la cubren y **delega la distribución a la",
|
|
5431
|
+
" herramienta `openteam-orchestrate`** con las asignaciones correspondientes.",
|
|
5432
|
+
" openteam se encarga del routing de modelo, la creación de subsesiones y la",
|
|
5433
|
+
" telemetría — tú no necesitas usar `task` directamente para roles del roster.",
|
|
5434
|
+
" Reutiliza el reparto existente. No vuelvas a castear ni recrear agentes.",
|
|
4540
5435
|
"- **Si NO existe equipo** (sin `openteam-roster.md` ni subagentes): **no pidas",
|
|
4541
5436
|
" permiso para crearlo**. Entiende primero las tareas que implica la",
|
|
4542
5437
|
" petición, diseña el equipo mínimo necesario, **créalo a demanda** y luego",
|
|
@@ -4637,6 +5532,39 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
4637
5532
|
"Explica brevemente al usuario tu plan (qué va en paralelo y qué en serie, y",
|
|
4638
5533
|
"por qué) antes o mientras lanzas las tareas.",
|
|
4639
5534
|
"",
|
|
5535
|
+
"## Distribución de trabajo con `openteam-orchestrate`",
|
|
5536
|
+
"",
|
|
5537
|
+
"Cuando el roster existe, **usa la herramienta `openteam-orchestrate`** para",
|
|
5538
|
+
"distribuir el trabajo entre los roles del equipo. Esta herramienta delega a",
|
|
5539
|
+
"nuestro código el routing de modelo por rol, la creación de subsesiones y la",
|
|
5540
|
+
"telemetría — no improvises estos pasos manualmente con `task`.",
|
|
5541
|
+
"",
|
|
5542
|
+
"Payload (las claves son estrictas — claves desconocidas se rechazan):",
|
|
5543
|
+
"",
|
|
5544
|
+
"```json",
|
|
5545
|
+
"{",
|
|
5546
|
+
' "assignments": [',
|
|
5547
|
+
' { "roleID": "<rol-del-roster>", "prompt": "<tarea accionable>", "title": "<título opcional>" },',
|
|
5548
|
+
' { "roleID": "<otro-rol>", "prompt": "<otra tarea>" }',
|
|
5549
|
+
" ],",
|
|
5550
|
+
' "parentSessionID": "<sesión actual, opcional>",',
|
|
5551
|
+
' "directory": "<directorio de trabajo, opcional>"',
|
|
5552
|
+
"}",
|
|
5553
|
+
"```",
|
|
5554
|
+
"",
|
|
5555
|
+
"- `assignments` (obligatorio): lista de asignaciones. Cada una tiene un",
|
|
5556
|
+
" `roleID` (debe coincidir con un rol del roster) y un `prompt` accionable.",
|
|
5557
|
+
" `title` es opcional y solo para legibilidad.",
|
|
5558
|
+
"- `parentSessionID` (opcional): la sesión actual, para que las subsesiones",
|
|
5559
|
+
" se vinculen como hijas.",
|
|
5560
|
+
"- `directory` (opcional): directorio de trabajo si difiere del actual.",
|
|
5561
|
+
"",
|
|
5562
|
+
"La herramienta devuelve un resumen por rol: modelo seleccionado, éxito o",
|
|
5563
|
+
"fallo, y sesión creada. Si un rol falla, el resto sigue ejecutándose.",
|
|
5564
|
+
"",
|
|
5565
|
+
"**Usa `task` directamente** solo para subagentes ad-hoc que no estén en el",
|
|
5566
|
+
"roster (p. ej. un agente creado a demanda para un trabajo puntual).",
|
|
5567
|
+
"",
|
|
4640
5568
|
"## Lista de tareas con responsable visible",
|
|
4641
5569
|
"",
|
|
4642
5570
|
"Mantén la lista de tareas de la sesión con `todowrite` y **haz visible quién",
|
|
@@ -4658,8 +5586,9 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
4658
5586
|
"",
|
|
4659
5587
|
"- **Empieza siempre comprobando el equipo** (roster + subagentes) antes de",
|
|
4660
5588
|
" delegar; si falta, créalo antes de repartir el trabajo.",
|
|
4661
|
-
"- Delega el trabajo con
|
|
4662
|
-
"
|
|
5589
|
+
"- Delega el trabajo del roster con `openteam-orchestrate`; usa `task` solo",
|
|
5590
|
+
" para subagentes ad-hoc que no tengan rol en el roster. Tú te mantienes como",
|
|
5591
|
+
" coordinador y no implementas lo que puede hacer un subagente.",
|
|
4663
5592
|
"- **Lanza en un solo turno las tareas independientes** para que corran en",
|
|
4664
5593
|
" paralelo; secuencia solo las que dependen de un resultado previo.",
|
|
4665
5594
|
"- Crea subagentes **justo cuando el trabajo lo pide**, nunca “por si acaso”.",
|
|
@@ -4953,7 +5882,10 @@ async function runCli(argv, deps) {
|
|
|
4953
5882
|
const config = await deps.loadConfig(configPath);
|
|
4954
5883
|
return { exitCode: 0, stdout: renderConsoleStatus(config.console) };
|
|
4955
5884
|
}
|
|
4956
|
-
if (command ===
|
|
5885
|
+
if (command === "--version" || command === "-v") {
|
|
5886
|
+
return { exitCode: 0, stdout: deps.version };
|
|
5887
|
+
}
|
|
5888
|
+
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
4957
5889
|
return { exitCode: 0, stdout: HELP };
|
|
4958
5890
|
}
|
|
4959
5891
|
return {
|
|
@@ -5560,54 +6492,54 @@ function loadOpenTeamConfig(raw) {
|
|
|
5560
6492
|
}
|
|
5561
6493
|
|
|
5562
6494
|
// src/memory/types.ts
|
|
5563
|
-
import { z as
|
|
6495
|
+
import { z as z8 } from "zod";
|
|
5564
6496
|
var SHARED_OWNER_KEY = "*";
|
|
5565
|
-
var OwnerKeySchema =
|
|
5566
|
-
var MemoryKindSchema =
|
|
5567
|
-
var MemoryBaseSchema =
|
|
5568
|
-
id:
|
|
6497
|
+
var OwnerKeySchema = z8.string().min(1);
|
|
6498
|
+
var MemoryKindSchema = z8.enum(["fact", "preference", "entity"]);
|
|
6499
|
+
var MemoryBaseSchema = z8.object({
|
|
6500
|
+
id: z8.string().min(1),
|
|
5569
6501
|
ownerKey: OwnerKeySchema,
|
|
5570
|
-
confidence:
|
|
5571
|
-
validFrom:
|
|
5572
|
-
validUntil:
|
|
5573
|
-
createdAt:
|
|
5574
|
-
invalidatedAt:
|
|
5575
|
-
sourceHash:
|
|
5576
|
-
supersededBy:
|
|
6502
|
+
confidence: z8.number().min(0).max(1),
|
|
6503
|
+
validFrom: z8.number().finite(),
|
|
6504
|
+
validUntil: z8.number().finite().nullable().default(null),
|
|
6505
|
+
createdAt: z8.number().finite(),
|
|
6506
|
+
invalidatedAt: z8.number().finite().nullable().default(null),
|
|
6507
|
+
sourceHash: z8.string().min(1),
|
|
6508
|
+
supersededBy: z8.string().min(1).nullable().default(null)
|
|
5577
6509
|
});
|
|
5578
6510
|
var FactSchema = MemoryBaseSchema.extend({
|
|
5579
|
-
kind:
|
|
5580
|
-
subject:
|
|
5581
|
-
predicate:
|
|
5582
|
-
object:
|
|
5583
|
-
category:
|
|
6511
|
+
kind: z8.literal("fact"),
|
|
6512
|
+
subject: z8.string().min(1),
|
|
6513
|
+
predicate: z8.string().min(1),
|
|
6514
|
+
object: z8.string().min(1),
|
|
6515
|
+
category: z8.string().min(1).nullable().default(null)
|
|
5584
6516
|
});
|
|
5585
6517
|
var PreferenceSchema = MemoryBaseSchema.extend({
|
|
5586
|
-
kind:
|
|
5587
|
-
category:
|
|
5588
|
-
preference:
|
|
5589
|
-
context:
|
|
5590
|
-
lastAccessedAt:
|
|
5591
|
-
accessCount:
|
|
6518
|
+
kind: z8.literal("preference"),
|
|
6519
|
+
category: z8.string().min(1),
|
|
6520
|
+
preference: z8.string().min(1),
|
|
6521
|
+
context: z8.string().min(1).nullable().default(null),
|
|
6522
|
+
lastAccessedAt: z8.number().finite().nullable().default(null),
|
|
6523
|
+
accessCount: z8.number().int().min(0).default(0)
|
|
5592
6524
|
});
|
|
5593
6525
|
var EntitySchema = MemoryBaseSchema.extend({
|
|
5594
|
-
kind:
|
|
5595
|
-
canonicalName:
|
|
5596
|
-
type:
|
|
5597
|
-
aliases:
|
|
6526
|
+
kind: z8.literal("entity"),
|
|
6527
|
+
canonicalName: z8.string().min(1),
|
|
6528
|
+
type: z8.string().min(1),
|
|
6529
|
+
aliases: z8.array(z8.string().min(1)).default([])
|
|
5598
6530
|
});
|
|
5599
|
-
var MemoryRecordSchema =
|
|
6531
|
+
var MemoryRecordSchema = z8.discriminatedUnion("kind", [
|
|
5600
6532
|
FactSchema,
|
|
5601
6533
|
PreferenceSchema,
|
|
5602
6534
|
EntitySchema
|
|
5603
6535
|
]);
|
|
5604
|
-
var RecallQuerySchema =
|
|
6536
|
+
var RecallQuerySchema = z8.object({
|
|
5605
6537
|
ownerKey: OwnerKeySchema,
|
|
5606
|
-
kinds:
|
|
5607
|
-
asOf:
|
|
5608
|
-
limit:
|
|
5609
|
-
minSimilarity:
|
|
5610
|
-
includeShared:
|
|
6538
|
+
kinds: z8.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
|
|
6539
|
+
asOf: z8.number().finite().nullable().default(null),
|
|
6540
|
+
limit: z8.number().int().positive().default(8),
|
|
6541
|
+
minSimilarity: z8.number().min(0).max(1).default(0.2),
|
|
6542
|
+
includeShared: z8.boolean().default(true)
|
|
5611
6543
|
});
|
|
5612
6544
|
|
|
5613
6545
|
// src/memory/rank.ts
|
|
@@ -6516,6 +7448,104 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
6516
7448
|
return;
|
|
6517
7449
|
}
|
|
6518
7450
|
}
|
|
7451
|
+
// package.json
|
|
7452
|
+
var package_default = {
|
|
7453
|
+
name: "@jmanuelcorral/openteam",
|
|
7454
|
+
version: "0.2.2",
|
|
7455
|
+
packageManager: "bun@1.3.14",
|
|
7456
|
+
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
7457
|
+
license: "MIT",
|
|
7458
|
+
author: "Jose Manuel Corral",
|
|
7459
|
+
repository: {
|
|
7460
|
+
type: "git",
|
|
7461
|
+
url: "git+https://github.com/jmanuelcorral/openteam.git"
|
|
7462
|
+
},
|
|
7463
|
+
homepage: "https://github.com/jmanuelcorral/openteam#readme",
|
|
7464
|
+
bugs: {
|
|
7465
|
+
url: "https://github.com/jmanuelcorral/openteam/issues"
|
|
7466
|
+
},
|
|
7467
|
+
keywords: [
|
|
7468
|
+
"opencode",
|
|
7469
|
+
"opencode-plugin",
|
|
7470
|
+
"llm",
|
|
7471
|
+
"routing",
|
|
7472
|
+
"local-llm",
|
|
7473
|
+
"ollama",
|
|
7474
|
+
"lm-studio",
|
|
7475
|
+
"foundry-local",
|
|
7476
|
+
"cost-optimization",
|
|
7477
|
+
"multi-agent"
|
|
7478
|
+
],
|
|
7479
|
+
engines: {
|
|
7480
|
+
bun: ">=1.3",
|
|
7481
|
+
node: "^22.22.2 || ^24.15.0 || >=26.0.0"
|
|
7482
|
+
},
|
|
7483
|
+
type: "module",
|
|
7484
|
+
main: "./dist/index.js",
|
|
7485
|
+
module: "./dist/index.js",
|
|
7486
|
+
types: "./dist/index.d.ts",
|
|
7487
|
+
bin: {
|
|
7488
|
+
openteam: "./dist/cli.js"
|
|
7489
|
+
},
|
|
7490
|
+
exports: {
|
|
7491
|
+
".": {
|
|
7492
|
+
types: "./dist/index.d.ts",
|
|
7493
|
+
import: "./dist/index.js"
|
|
7494
|
+
},
|
|
7495
|
+
"./package.json": "./package.json"
|
|
7496
|
+
},
|
|
7497
|
+
files: [
|
|
7498
|
+
"dist",
|
|
7499
|
+
"README.md",
|
|
7500
|
+
"LICENSE",
|
|
7501
|
+
"AGENTS.md",
|
|
7502
|
+
".opencode/openteam.example.json",
|
|
7503
|
+
".opencode/command/openteam.md"
|
|
7504
|
+
],
|
|
7505
|
+
publishConfig: {
|
|
7506
|
+
access: "public",
|
|
7507
|
+
registry: "https://registry.npmjs.org/"
|
|
7508
|
+
},
|
|
7509
|
+
trustedDependencies: [],
|
|
7510
|
+
sideEffects: false,
|
|
7511
|
+
scripts: {
|
|
7512
|
+
prebuild: "bun run clean",
|
|
7513
|
+
build: "bun run build:js && bun run build:cli && bun run build:types",
|
|
7514
|
+
"build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod",
|
|
7515
|
+
"build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts',
|
|
7516
|
+
"build:types": "tsc -p tsconfig.build.json",
|
|
7517
|
+
clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
|
|
7518
|
+
test: "bun test",
|
|
7519
|
+
"test:cov": "bun test --coverage",
|
|
7520
|
+
"certify:shadow": "bun test tests/certification/graph-shadow.test.ts",
|
|
7521
|
+
"certify:release": "bun test tests/certification/graph-release.test.ts",
|
|
7522
|
+
"coverage:check": "node scripts/check-coverage.mjs",
|
|
7523
|
+
typecheck: "tsc --noEmit",
|
|
7524
|
+
lint: "biome check .",
|
|
7525
|
+
"format:check": "biome format .",
|
|
7526
|
+
"docs:install": "cd docs && bun install --frozen-lockfile --ignore-scripts",
|
|
7527
|
+
"docs:dev": "cd docs && bun run docs:dev",
|
|
7528
|
+
"docs:build": "cd docs && bun run docs:build",
|
|
7529
|
+
"docs:preview": "cd docs && bun run docs:preview",
|
|
7530
|
+
prepublishOnly: "bun run build",
|
|
7531
|
+
"link:local": "bun run build && npm link",
|
|
7532
|
+
"hooks:install": "git config core.hooksPath .githooks"
|
|
7533
|
+
},
|
|
7534
|
+
dependencies: {
|
|
7535
|
+
"@clack/prompts": "1.7.0",
|
|
7536
|
+
"@opencode-ai/plugin": "1.18.18",
|
|
7537
|
+
"@opencode-ai/sdk": "1.18.18",
|
|
7538
|
+
zod: "4.4.3"
|
|
7539
|
+
},
|
|
7540
|
+
devDependencies: {
|
|
7541
|
+
"@biomejs/biome": "2.5.9",
|
|
7542
|
+
"@types/bun": "1.3.14",
|
|
7543
|
+
typescript: "7.0.2"
|
|
7544
|
+
}
|
|
7545
|
+
};
|
|
7546
|
+
|
|
7547
|
+
// src/version.ts
|
|
7548
|
+
var PACKAGE_VERSION = package_default.version;
|
|
6519
7549
|
|
|
6520
7550
|
// src/web/git.ts
|
|
6521
7551
|
function createGitLastCommit(exec) {
|
|
@@ -6623,7 +7653,8 @@ var deps = {
|
|
|
6623
7653
|
telemetryPath: DEFAULT_TELEMETRY_PATH,
|
|
6624
7654
|
opencodeConfigPath: OPENCODE_CONFIG_PATH,
|
|
6625
7655
|
orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH,
|
|
6626
|
-
agentDir: AGENT_DIR
|
|
7656
|
+
agentDir: AGENT_DIR,
|
|
7657
|
+
version: PACKAGE_VERSION
|
|
6627
7658
|
};
|
|
6628
7659
|
function createSetupDeps() {
|
|
6629
7660
|
return {
|