@amistio/cli 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/index.js +859 -86
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { createHash as
|
|
4
|
+
import { createHash as createHash7, randomUUID } from "node:crypto";
|
|
5
5
|
import { writeFile as writeFile9 } from "node:fs/promises";
|
|
6
6
|
import os7 from "node:os";
|
|
7
7
|
import path13 from "node:path";
|
|
@@ -120,13 +120,55 @@ var runnerToolNameSchema = z.enum(runnerToolNames);
|
|
|
120
120
|
var runnerToolSelectionSchema = z.union([runnerToolNameSchema, z.literal("auto")]);
|
|
121
121
|
var runnerPreferenceScopeSchema = z.enum(["account", "project"]);
|
|
122
122
|
var runnerPreferenceSourceSchema = z.enum(["cli", "project", "account", "default"]);
|
|
123
|
-
var runnerPreferenceStatusSchema = z.enum(["resolved", "unavailable", "modelUnsupported", "channelUnsupported", "custom", "none"]);
|
|
123
|
+
var runnerPreferenceStatusSchema = z.enum(["resolved", "unavailable", "modelUnsupported", "variantUnsupported", "reasoningUnsupported", "channelUnsupported", "custom", "none"]);
|
|
124
124
|
var runnerInvocationChannelSchema = z.enum(["auto", "sdk", "command"]);
|
|
125
125
|
var runnerEffectiveInvocationChannelSchema = z.enum(["sdk", "command"]);
|
|
126
|
+
var runnerReasoningEffortSchema = z.enum(["auto", "low", "medium", "high", "xhigh"]);
|
|
127
|
+
var runnerProviderModelStatusSchema = z.enum(["alpha", "beta", "deprecated", "active"]);
|
|
128
|
+
var runnerProviderIdSchema = z.string().trim().min(1).max(120);
|
|
129
|
+
var runnerProviderModelIdSchema = z.string().trim().min(1).max(200);
|
|
130
|
+
var runnerProviderModelLimitSchema = z.object({
|
|
131
|
+
context: z.number().nonnegative().optional(),
|
|
132
|
+
input: z.number().nonnegative().optional(),
|
|
133
|
+
output: z.number().nonnegative().optional()
|
|
134
|
+
}).strict();
|
|
135
|
+
var runnerProviderModelModalitiesSchema = z.object({
|
|
136
|
+
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])).max(12).optional(),
|
|
137
|
+
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])).max(12).optional()
|
|
138
|
+
}).strict();
|
|
139
|
+
var runnerProviderModelVariantSchema = z.object({
|
|
140
|
+
disabled: z.boolean().optional()
|
|
141
|
+
}).strict();
|
|
142
|
+
var runnerProviderModelSchema = z.object({
|
|
143
|
+
id: runnerProviderModelIdSchema.optional(),
|
|
144
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
145
|
+
family: z.string().trim().min(1).max(120).optional(),
|
|
146
|
+
releaseDate: z.string().trim().min(1).max(40).optional(),
|
|
147
|
+
attachment: z.boolean().optional(),
|
|
148
|
+
reasoning: z.boolean().optional(),
|
|
149
|
+
temperature: z.boolean().optional(),
|
|
150
|
+
toolCall: z.boolean().optional(),
|
|
151
|
+
status: runnerProviderModelStatusSchema.optional(),
|
|
152
|
+
limit: runnerProviderModelLimitSchema.optional(),
|
|
153
|
+
modalities: runnerProviderModelModalitiesSchema.optional(),
|
|
154
|
+
supportedReasoningEfforts: z.array(runnerReasoningEffortSchema).max(8).optional(),
|
|
155
|
+
variants: z.record(z.string().trim().min(1).max(120), runnerProviderModelVariantSchema).optional()
|
|
156
|
+
}).strict();
|
|
157
|
+
var runnerProviderConfigSchema = z.object({
|
|
158
|
+
id: runnerProviderIdSchema.optional(),
|
|
159
|
+
name: z.string().trim().min(1).max(160).optional(),
|
|
160
|
+
api: z.string().trim().min(1).max(80).optional(),
|
|
161
|
+
models: z.record(runnerProviderModelIdSchema, runnerProviderModelSchema).default({})
|
|
162
|
+
}).strict();
|
|
163
|
+
var runnerProviderCatalogSchema = z.record(runnerProviderIdSchema, runnerProviderConfigSchema);
|
|
126
164
|
var runnerToolModelPreferenceSchema = z.object({
|
|
127
165
|
tool: runnerToolSelectionSchema.optional(),
|
|
128
166
|
invocationChannel: runnerInvocationChannelSchema.optional(),
|
|
129
|
-
model: z.string().trim().min(1).max(160).optional()
|
|
167
|
+
model: z.string().trim().min(1).max(160).optional(),
|
|
168
|
+
providerId: runnerProviderIdSchema.optional(),
|
|
169
|
+
modelId: runnerProviderModelIdSchema.optional(),
|
|
170
|
+
modelVariant: z.string().trim().min(1).max(120).optional(),
|
|
171
|
+
reasoningEffort: runnerReasoningEffortSchema.optional()
|
|
130
172
|
});
|
|
131
173
|
var runnerToolCapabilitySchema = z.object({
|
|
132
174
|
name: runnerToolNameSchema,
|
|
@@ -138,6 +180,7 @@ var runnerToolCapabilitySchema = z.object({
|
|
|
138
180
|
supportsSessionReuse: z.boolean(),
|
|
139
181
|
resumabilityScope: sessionResumabilityScopeSchema,
|
|
140
182
|
supportsModelSelection: z.boolean(),
|
|
183
|
+
providerCatalog: runnerProviderCatalogSchema.optional(),
|
|
141
184
|
supportsBranchIsolation: z.boolean().optional(),
|
|
142
185
|
supportsGitWorktreeIsolation: z.boolean().optional()
|
|
143
186
|
});
|
|
@@ -159,6 +202,7 @@ var runnerResourceUsageSchema = z.object({
|
|
|
159
202
|
var workIsolationModeSchema = z.enum(["none", "primaryCheckout", "branch", "gitWorktree"]);
|
|
160
203
|
var repositoryLinkSourceSchema = z.enum(["web", "cli"]);
|
|
161
204
|
var repositoryCloneStatusSchema = z.enum(["notCloned", "cloned", "validated", "failed"]);
|
|
205
|
+
var repositoryBrainAutoSyncStatusSchema = z.enum(["disabled", "enabledInactive", "active", "synced", "failed", "blocked", "conflicted"]);
|
|
162
206
|
var projectStatusSchema = z.enum(["active", "archived"]);
|
|
163
207
|
var workspaceScopeKindSchema = z.enum(["personal", "organization"]);
|
|
164
208
|
var personalWorkspaceScopeSchema = z.object({
|
|
@@ -229,6 +273,7 @@ var repositoryLinkItemSchema = baseItemSchema.extend({
|
|
|
229
273
|
linkedByUserId: z.string().min(1).optional(),
|
|
230
274
|
linkSource: repositoryLinkSourceSchema.optional(),
|
|
231
275
|
cloneStatus: repositoryCloneStatusSchema.optional(),
|
|
276
|
+
autoSyncEnabled: z.boolean().optional(),
|
|
232
277
|
lastValidatedAt: isoDateTimeSchema.optional(),
|
|
233
278
|
status: z.enum(["active", "revoked"]).default("active")
|
|
234
279
|
});
|
|
@@ -350,13 +395,29 @@ var runnerHeartbeatItemSchema = baseItemSchema.extend({
|
|
|
350
395
|
capabilities: z.array(runnerToolCapabilitySchema).optional(),
|
|
351
396
|
requestedTool: runnerToolSelectionSchema.optional(),
|
|
352
397
|
requestedInvocationChannel: runnerInvocationChannelSchema.optional(),
|
|
398
|
+
requestedProviderId: runnerProviderIdSchema.optional(),
|
|
399
|
+
requestedModelId: runnerProviderModelIdSchema.optional(),
|
|
400
|
+
requestedModelVariant: z.string().trim().min(1).max(120).optional(),
|
|
401
|
+
requestedReasoningEffort: runnerReasoningEffortSchema.optional(),
|
|
353
402
|
effectiveTool: z.union([runnerToolNameSchema, z.literal("custom")]).optional(),
|
|
354
403
|
effectiveInvocationChannel: runnerEffectiveInvocationChannelSchema.optional(),
|
|
355
404
|
effectiveModel: z.string().min(1).optional(),
|
|
405
|
+
effectiveProviderId: runnerProviderIdSchema.optional(),
|
|
406
|
+
effectiveModelId: runnerProviderModelIdSchema.optional(),
|
|
407
|
+
effectiveModelVariant: z.string().trim().min(1).max(120).optional(),
|
|
408
|
+
effectiveReasoningEffort: runnerReasoningEffortSchema.optional(),
|
|
356
409
|
preferenceSource: runnerPreferenceSourceSchema.optional(),
|
|
357
410
|
preferenceStatus: runnerPreferenceStatusSchema.optional(),
|
|
358
411
|
preferenceMessage: z.string().optional(),
|
|
359
412
|
resourceUsage: runnerResourceUsageSchema.optional(),
|
|
413
|
+
autoSyncStatus: repositoryBrainAutoSyncStatusSchema.optional(),
|
|
414
|
+
autoSyncMessage: z.string().max(400).optional(),
|
|
415
|
+
autoSyncLastStartedAt: isoDateTimeSchema.optional(),
|
|
416
|
+
autoSyncLastSuccessAt: isoDateTimeSchema.optional(),
|
|
417
|
+
autoSyncLastFailureAt: isoDateTimeSchema.optional(),
|
|
418
|
+
autoSyncPushedCount: z.number().int().nonnegative().optional(),
|
|
419
|
+
autoSyncSkippedCount: z.number().int().nonnegative().optional(),
|
|
420
|
+
autoSyncConflictCount: z.number().int().nonnegative().optional(),
|
|
360
421
|
lastSeenAt: isoDateTimeSchema
|
|
361
422
|
});
|
|
362
423
|
var runnerSettingsItemSchema = baseItemSchema.extend({
|
|
@@ -1436,6 +1497,13 @@ var ApiClient = class {
|
|
|
1436
1497
|
{ method: "GET" }
|
|
1437
1498
|
);
|
|
1438
1499
|
}
|
|
1500
|
+
async listRepositoryLinks(projectId) {
|
|
1501
|
+
return this.request(
|
|
1502
|
+
`/projects/${projectId}/repository-links`,
|
|
1503
|
+
z3.object({ repositoryLinks: z3.array(repositoryLinkItemSchema) }),
|
|
1504
|
+
{ method: "GET" }
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1439
1507
|
async listPlanReviewMessages(projectId, documentId) {
|
|
1440
1508
|
const suffix = documentId ? `?documentId=${encodeURIComponent(documentId)}` : "";
|
|
1441
1509
|
return this.request(
|
|
@@ -1503,6 +1571,10 @@ var ApiClient = class {
|
|
|
1503
1571
|
tool: runnerToolSelectionSchema,
|
|
1504
1572
|
invocationChannel: runnerInvocationChannelSchema,
|
|
1505
1573
|
model: z3.string().optional(),
|
|
1574
|
+
providerId: z3.string().optional(),
|
|
1575
|
+
modelId: z3.string().optional(),
|
|
1576
|
+
modelVariant: z3.string().optional(),
|
|
1577
|
+
reasoningEffort: runnerReasoningEffortSchema.optional(),
|
|
1506
1578
|
source: runnerPreferenceSourceSchema
|
|
1507
1579
|
})
|
|
1508
1580
|
}),
|
|
@@ -1725,10 +1797,87 @@ async function writePromptFile(filePath, prompt) {
|
|
|
1725
1797
|
|
|
1726
1798
|
// src/local-tool-runner.ts
|
|
1727
1799
|
import { spawn } from "node:child_process";
|
|
1728
|
-
import { mkdtemp, rm, writeFile as writeFile4 } from "node:fs/promises";
|
|
1800
|
+
import { mkdtemp, readFile as readFile3, rm, writeFile as writeFile4 } from "node:fs/promises";
|
|
1729
1801
|
import os2 from "node:os";
|
|
1730
1802
|
import path5 from "node:path";
|
|
1731
1803
|
var localToolNames = runnerToolNames;
|
|
1804
|
+
var allReasoningEfforts = ["auto", "low", "medium", "high", "xhigh"];
|
|
1805
|
+
var highReasoningEfforts = ["auto", "high", "xhigh"];
|
|
1806
|
+
var builtInProviderCatalogs = {
|
|
1807
|
+
opencode: {
|
|
1808
|
+
anthropic: {
|
|
1809
|
+
id: "anthropic",
|
|
1810
|
+
name: "Anthropic",
|
|
1811
|
+
api: "anthropic",
|
|
1812
|
+
models: {
|
|
1813
|
+
"claude-opus-4.6": reasoningModel("claude-opus-4.6", "Claude Opus 4.6", "claude", highReasoningEfforts),
|
|
1814
|
+
"claude-sonnet-4.5": reasoningModel("claude-sonnet-4.5", "Claude Sonnet 4.5", "claude", highReasoningEfforts)
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
openai: {
|
|
1818
|
+
id: "openai",
|
|
1819
|
+
name: "OpenAI",
|
|
1820
|
+
api: "openai",
|
|
1821
|
+
models: {
|
|
1822
|
+
"gpt-5": reasoningModel("gpt-5", "GPT-5", "gpt", allReasoningEfforts)
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
},
|
|
1826
|
+
claude: {
|
|
1827
|
+
anthropic: {
|
|
1828
|
+
id: "anthropic",
|
|
1829
|
+
name: "Anthropic",
|
|
1830
|
+
api: "anthropic",
|
|
1831
|
+
models: {
|
|
1832
|
+
"claude-opus-4.6": reasoningModel("claude-opus-4.6", "Claude Opus 4.6", "claude", highReasoningEfforts),
|
|
1833
|
+
"claude-sonnet-4.5": reasoningModel("claude-sonnet-4.5", "Claude Sonnet 4.5", "claude", highReasoningEfforts)
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
},
|
|
1837
|
+
codex: {
|
|
1838
|
+
openai: {
|
|
1839
|
+
id: "openai",
|
|
1840
|
+
name: "OpenAI",
|
|
1841
|
+
api: "openai",
|
|
1842
|
+
models: {
|
|
1843
|
+
"gpt-5": reasoningModel("gpt-5", "GPT-5", "gpt", allReasoningEfforts)
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
},
|
|
1847
|
+
copilot: {
|
|
1848
|
+
"github-copilot": {
|
|
1849
|
+
id: "github-copilot",
|
|
1850
|
+
name: "GitHub Copilot",
|
|
1851
|
+
api: "copilot",
|
|
1852
|
+
models: {
|
|
1853
|
+
"gpt-5": reasoningModel("gpt-5", "GPT-5", "gpt", allReasoningEfforts),
|
|
1854
|
+
"claude-opus-4.6": reasoningModel("claude-opus-4.6", "Claude Opus 4.6", "claude", highReasoningEfforts)
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
},
|
|
1858
|
+
gemini: {
|
|
1859
|
+
google: {
|
|
1860
|
+
id: "google",
|
|
1861
|
+
name: "Google",
|
|
1862
|
+
api: "google",
|
|
1863
|
+
models: {
|
|
1864
|
+
"gemini-3-pro": reasoningModel("gemini-3-pro", "Gemini 3 Pro", "gemini", highReasoningEfforts)
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
function reasoningModel(id, name, family, supportedReasoningEfforts) {
|
|
1870
|
+
return {
|
|
1871
|
+
id,
|
|
1872
|
+
name,
|
|
1873
|
+
family,
|
|
1874
|
+
status: "active",
|
|
1875
|
+
reasoning: true,
|
|
1876
|
+
toolCall: true,
|
|
1877
|
+
supportedReasoningEfforts,
|
|
1878
|
+
variants: { standard: {} }
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1732
1881
|
var localToolAdapters = [
|
|
1733
1882
|
{
|
|
1734
1883
|
name: "opencode",
|
|
@@ -1737,13 +1886,15 @@ var localToolAdapters = [
|
|
|
1737
1886
|
sdkDisplayCommand: "@opencode-ai/sdk createOpencode().client.session.prompt()",
|
|
1738
1887
|
sdkRequiresExecutable: true,
|
|
1739
1888
|
executable: "opencode",
|
|
1889
|
+
supportsModelSelection: true,
|
|
1740
1890
|
supportsSessionReuse: true,
|
|
1741
1891
|
resumabilityScope: "localMachine",
|
|
1892
|
+
providerCatalog: builtInProviderCatalogs.opencode,
|
|
1742
1893
|
runWithSdk: runOpencodeSdk,
|
|
1743
|
-
buildInvocation: ({ prompt }) => ({
|
|
1894
|
+
buildInvocation: ({ prompt, model }) => ({
|
|
1744
1895
|
command: "opencode",
|
|
1745
|
-
args: ["run", prompt],
|
|
1746
|
-
displayCommand: "opencode run <generated prompt>"
|
|
1896
|
+
args: ["run", ...model ? ["--model", model] : [], prompt],
|
|
1897
|
+
displayCommand: model ? "opencode run --model <selected model> <generated prompt>" : "opencode run <generated prompt>"
|
|
1747
1898
|
})
|
|
1748
1899
|
},
|
|
1749
1900
|
{
|
|
@@ -1752,13 +1903,15 @@ var localToolAdapters = [
|
|
|
1752
1903
|
sdkPackageName: "@anthropic-ai/claude-agent-sdk",
|
|
1753
1904
|
sdkDisplayCommand: "@anthropic-ai/claude-agent-sdk query()",
|
|
1754
1905
|
executable: "claude",
|
|
1906
|
+
supportsModelSelection: true,
|
|
1755
1907
|
supportsSessionReuse: false,
|
|
1756
1908
|
resumabilityScope: "none",
|
|
1909
|
+
providerCatalog: builtInProviderCatalogs.claude,
|
|
1757
1910
|
runWithSdk: runClaudeSdk,
|
|
1758
|
-
buildInvocation: ({ prompt }) => ({
|
|
1911
|
+
buildInvocation: ({ prompt, model }) => ({
|
|
1759
1912
|
command: "claude",
|
|
1760
|
-
args: ["-p", prompt],
|
|
1761
|
-
displayCommand: "claude -p <generated prompt>"
|
|
1913
|
+
args: [...model ? ["--model", model] : [], "-p", prompt],
|
|
1914
|
+
displayCommand: model ? "claude --model <selected model> -p <generated prompt>" : "claude -p <generated prompt>"
|
|
1762
1915
|
})
|
|
1763
1916
|
},
|
|
1764
1917
|
{
|
|
@@ -1767,13 +1920,15 @@ var localToolAdapters = [
|
|
|
1767
1920
|
sdkPackageName: "@openai/codex-sdk",
|
|
1768
1921
|
sdkDisplayCommand: "@openai/codex-sdk Codex.startThread().run()",
|
|
1769
1922
|
executable: "codex",
|
|
1923
|
+
supportsModelSelection: true,
|
|
1770
1924
|
supportsSessionReuse: false,
|
|
1771
1925
|
resumabilityScope: "none",
|
|
1926
|
+
providerCatalog: builtInProviderCatalogs.codex,
|
|
1772
1927
|
runWithSdk: runCodexSdk,
|
|
1773
|
-
buildInvocation: ({ prompt }) => ({
|
|
1928
|
+
buildInvocation: ({ prompt, model }) => ({
|
|
1774
1929
|
command: "codex",
|
|
1775
|
-
args: ["exec", prompt],
|
|
1776
|
-
displayCommand: "codex exec <generated prompt>"
|
|
1930
|
+
args: ["exec", ...model ? ["--model", model] : [], prompt],
|
|
1931
|
+
displayCommand: model ? "codex exec --model <selected model> <generated prompt>" : "codex exec <generated prompt>"
|
|
1777
1932
|
})
|
|
1778
1933
|
},
|
|
1779
1934
|
{
|
|
@@ -1784,18 +1939,21 @@ var localToolAdapters = [
|
|
|
1784
1939
|
supportsModelSelection: true,
|
|
1785
1940
|
supportsSessionReuse: false,
|
|
1786
1941
|
resumabilityScope: "none",
|
|
1942
|
+
providerCatalog: builtInProviderCatalogs.copilot,
|
|
1787
1943
|
runWithSdk: runCopilotSdk
|
|
1788
1944
|
},
|
|
1789
1945
|
{
|
|
1790
1946
|
name: "gemini",
|
|
1791
1947
|
description: "Gemini CLI adapter using prompt mode.",
|
|
1792
1948
|
executable: "gemini",
|
|
1949
|
+
supportsModelSelection: true,
|
|
1793
1950
|
supportsSessionReuse: false,
|
|
1794
1951
|
resumabilityScope: "none",
|
|
1795
|
-
|
|
1952
|
+
providerCatalog: builtInProviderCatalogs.gemini,
|
|
1953
|
+
buildInvocation: ({ prompt, model }) => ({
|
|
1796
1954
|
command: "gemini",
|
|
1797
|
-
args: ["-p", prompt],
|
|
1798
|
-
displayCommand: "gemini -p <generated prompt>"
|
|
1955
|
+
args: [...model ? ["--model", model] : [], "-p", prompt],
|
|
1956
|
+
displayCommand: model ? "gemini --model <selected model> -p <generated prompt>" : "gemini -p <generated prompt>"
|
|
1799
1957
|
})
|
|
1800
1958
|
},
|
|
1801
1959
|
{
|
|
@@ -1831,6 +1989,7 @@ async function detectLocalTools() {
|
|
|
1831
1989
|
localToolAdapters.map(async (adapter) => {
|
|
1832
1990
|
const sdkAvailable = await isSdkAvailable(adapter);
|
|
1833
1991
|
const commandAvailable = adapter.executable ? await commandExists(adapter.executable) : false;
|
|
1992
|
+
const providerCatalog = await detectProviderCatalog(adapter);
|
|
1834
1993
|
return {
|
|
1835
1994
|
name: adapter.name,
|
|
1836
1995
|
description: adapter.description,
|
|
@@ -1840,7 +1999,8 @@ async function detectLocalTools() {
|
|
|
1840
1999
|
execution: sdkAvailable ? "sdk" : commandAvailable ? "command" : "unavailable",
|
|
1841
2000
|
supportsSessionReuse: Boolean(adapter.supportsSessionReuse),
|
|
1842
2001
|
resumabilityScope: adapter.resumabilityScope ?? "none",
|
|
1843
|
-
supportsModelSelection: Boolean(adapter.supportsModelSelection)
|
|
2002
|
+
supportsModelSelection: Boolean(adapter.supportsModelSelection),
|
|
2003
|
+
...providerCatalog ? { providerCatalog } : {}
|
|
1844
2004
|
};
|
|
1845
2005
|
})
|
|
1846
2006
|
);
|
|
@@ -1849,6 +2009,7 @@ async function runLocalTool(options) {
|
|
|
1849
2009
|
const promptTempDir = await mkdtemp(path5.join(os2.tmpdir(), "amistio-prompt-"));
|
|
1850
2010
|
const promptFilePath = path5.join(promptTempDir, "prompt.md");
|
|
1851
2011
|
await writeFile4(promptFilePath, options.prompt, "utf8");
|
|
2012
|
+
const modelConfig = normalizeModelOptions(options);
|
|
1852
2013
|
try {
|
|
1853
2014
|
const runnerOptions = {
|
|
1854
2015
|
rootDir: options.rootDir,
|
|
@@ -1856,7 +2017,7 @@ async function runLocalTool(options) {
|
|
|
1856
2017
|
promptFilePath,
|
|
1857
2018
|
tool: options.tool ?? "auto",
|
|
1858
2019
|
invocationChannel: options.invocationChannel ?? "auto",
|
|
1859
|
-
...
|
|
2020
|
+
...modelConfig
|
|
1860
2021
|
};
|
|
1861
2022
|
if (options.toolCommand) {
|
|
1862
2023
|
runnerOptions.toolCommand = options.toolCommand;
|
|
@@ -1867,6 +2028,7 @@ async function runLocalTool(options) {
|
|
|
1867
2028
|
prompt: options.prompt,
|
|
1868
2029
|
promptFilePath,
|
|
1869
2030
|
streamOutput: Boolean(options.streamOutput),
|
|
2031
|
+
...modelConfig,
|
|
1870
2032
|
...options.session ? { session: options.session } : {}
|
|
1871
2033
|
}, options.timeoutMs);
|
|
1872
2034
|
return {
|
|
@@ -1874,7 +2036,7 @@ async function runLocalTool(options) {
|
|
|
1874
2036
|
displayCommand: runner2.kind === "sdk" ? runner2.displayCommand : runner2.invocation.displayCommand,
|
|
1875
2037
|
supportsSessionReuse: runner2.kind === "sdk" ? Boolean(runner2.adapter.supportsSessionReuse) : false,
|
|
1876
2038
|
resumabilityScope: runner2.kind === "sdk" ? runner2.adapter.resumabilityScope ?? "none" : "none",
|
|
1877
|
-
...
|
|
2039
|
+
...modelConfig,
|
|
1878
2040
|
...result
|
|
1879
2041
|
};
|
|
1880
2042
|
} finally {
|
|
@@ -1883,13 +2045,14 @@ async function runLocalTool(options) {
|
|
|
1883
2045
|
}
|
|
1884
2046
|
async function createToolRunPreview(options) {
|
|
1885
2047
|
const promptFilePath = path5.join(os2.tmpdir(), "amistio-generated-prompt.md");
|
|
2048
|
+
const modelConfig = normalizeModelOptions(options);
|
|
1886
2049
|
const runnerOptions = {
|
|
1887
2050
|
rootDir: options.rootDir,
|
|
1888
2051
|
prompt: options.prompt,
|
|
1889
2052
|
promptFilePath,
|
|
1890
2053
|
tool: options.tool ?? "auto",
|
|
1891
2054
|
invocationChannel: options.invocationChannel ?? "auto",
|
|
1892
|
-
...
|
|
2055
|
+
...modelConfig
|
|
1893
2056
|
};
|
|
1894
2057
|
if (options.toolCommand) {
|
|
1895
2058
|
runnerOptions.toolCommand = options.toolCommand;
|
|
@@ -1900,7 +2063,20 @@ async function createToolRunPreview(options) {
|
|
|
1900
2063
|
displayCommand: runner2.kind === "sdk" ? runner2.displayCommand : runner2.invocation.displayCommand,
|
|
1901
2064
|
supportsSessionReuse: runner2.kind === "sdk" ? Boolean(runner2.adapter.supportsSessionReuse) : false,
|
|
1902
2065
|
resumabilityScope: runner2.kind === "sdk" ? runner2.adapter.resumabilityScope ?? "none" : "none",
|
|
1903
|
-
...
|
|
2066
|
+
...modelConfig
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function normalizeModelOptions(options) {
|
|
2070
|
+
const model = options.model?.trim() || (options.providerId?.trim() && options.modelId?.trim() ? `${options.providerId.trim()}/${options.modelId.trim()}` : options.modelId?.trim()) || void 0;
|
|
2071
|
+
const providerId = options.providerId?.trim();
|
|
2072
|
+
const modelId = options.modelId?.trim();
|
|
2073
|
+
const modelVariant = options.modelVariant?.trim();
|
|
2074
|
+
return {
|
|
2075
|
+
...model ? { model } : {},
|
|
2076
|
+
...providerId ? { providerId } : {},
|
|
2077
|
+
...modelId ? { modelId } : {},
|
|
2078
|
+
...modelVariant ? { modelVariant } : {},
|
|
2079
|
+
...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}
|
|
1904
2080
|
};
|
|
1905
2081
|
}
|
|
1906
2082
|
function createCustomToolInvocation(commandTemplate, input) {
|
|
@@ -1925,9 +2101,13 @@ async function createToolRunner(options) {
|
|
|
1925
2101
|
if (tool === "none") {
|
|
1926
2102
|
throw new Error("No local tool selected. Use --tool auto, a supported tool name, or --tool-command.");
|
|
1927
2103
|
}
|
|
1928
|
-
const
|
|
1929
|
-
if (
|
|
1930
|
-
throw new Error(
|
|
2104
|
+
const requiresModelSelection = Boolean(options.model || options.providerId || options.modelId || options.modelVariant || options.reasoningEffort && options.reasoningEffort !== "auto");
|
|
2105
|
+
if (requiresModelSelection && !options.model) {
|
|
2106
|
+
throw new Error("Provider-backed model configuration requires --model or --provider with --model-id.");
|
|
2107
|
+
}
|
|
2108
|
+
const adapter = tool === "auto" ? await selectFirstAvailableAdapter(requiresModelSelection, options.invocationChannel) : await selectRequestedAdapter(tool, options.invocationChannel);
|
|
2109
|
+
if (requiresModelSelection && !adapter.supportsModelSelection) {
|
|
2110
|
+
throw new Error(`Model selection is not supported by ${adapter.name}. Remove model configuration or choose a model-aware adapter.`);
|
|
1931
2111
|
}
|
|
1932
2112
|
if (options.invocationChannel !== "command" && adapter.runWithSdk && await isSdkAvailable(adapter)) {
|
|
1933
2113
|
return {
|
|
@@ -2051,6 +2231,147 @@ async function commandExists(command) {
|
|
|
2051
2231
|
lookup.on("close", (exitCode) => resolve(exitCode === 0));
|
|
2052
2232
|
});
|
|
2053
2233
|
}
|
|
2234
|
+
async function detectProviderCatalog(adapter) {
|
|
2235
|
+
const opencodeCatalog = adapter.name === "opencode" ? await loadOpencodeProviderCatalog() : void 0;
|
|
2236
|
+
return mergeProviderCatalogs(adapter.providerCatalog, opencodeCatalog);
|
|
2237
|
+
}
|
|
2238
|
+
async function loadOpencodeProviderCatalog() {
|
|
2239
|
+
const configPaths = [
|
|
2240
|
+
path5.join(os2.homedir(), ".config", "opencode", "opencode.json"),
|
|
2241
|
+
path5.join(os2.homedir(), ".config", "opencode", "config.json"),
|
|
2242
|
+
path5.join(process.cwd(), "opencode.json")
|
|
2243
|
+
];
|
|
2244
|
+
for (const configPath of configPaths) {
|
|
2245
|
+
try {
|
|
2246
|
+
const parsed = JSON.parse(await readFile3(configPath, "utf8"));
|
|
2247
|
+
const providerValue = isRecord(parsed) ? parsed.provider : void 0;
|
|
2248
|
+
const catalog = sanitizeProviderCatalog(providerValue);
|
|
2249
|
+
if (catalog) return catalog;
|
|
2250
|
+
} catch {
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return void 0;
|
|
2254
|
+
}
|
|
2255
|
+
function mergeProviderCatalogs(...catalogs) {
|
|
2256
|
+
const merged = {};
|
|
2257
|
+
for (const catalog of catalogs) {
|
|
2258
|
+
if (!catalog) continue;
|
|
2259
|
+
for (const [providerId, provider] of Object.entries(catalog)) {
|
|
2260
|
+
const existing = isRecord(merged[providerId]) ? merged[providerId] : void 0;
|
|
2261
|
+
const existingModels = existing && isRecord(existing.models) ? existing.models : {};
|
|
2262
|
+
merged[providerId] = {
|
|
2263
|
+
...existing,
|
|
2264
|
+
...provider,
|
|
2265
|
+
id: provider.id ?? providerId,
|
|
2266
|
+
models: { ...existingModels, ...provider.models }
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
const parsed = runnerProviderCatalogSchema.safeParse(merged);
|
|
2271
|
+
return parsed.success && Object.keys(parsed.data).length ? parsed.data : void 0;
|
|
2272
|
+
}
|
|
2273
|
+
function sanitizeProviderCatalog(value) {
|
|
2274
|
+
if (!isRecord(value)) return void 0;
|
|
2275
|
+
const catalog = {};
|
|
2276
|
+
for (const [providerId, providerValue] of Object.entries(value)) {
|
|
2277
|
+
const provider = sanitizeProviderConfig(providerId, providerValue);
|
|
2278
|
+
if (provider) catalog[providerId] = provider;
|
|
2279
|
+
}
|
|
2280
|
+
const parsed = runnerProviderCatalogSchema.safeParse(catalog);
|
|
2281
|
+
return parsed.success && Object.keys(parsed.data).length ? parsed.data : void 0;
|
|
2282
|
+
}
|
|
2283
|
+
function sanitizeProviderConfig(providerId, value) {
|
|
2284
|
+
if (!isRecord(value)) return void 0;
|
|
2285
|
+
const models = sanitizeProviderModels(value.models);
|
|
2286
|
+
if (!Object.keys(models).length) return void 0;
|
|
2287
|
+
return {
|
|
2288
|
+
id: stringValue(value.id) ?? providerId,
|
|
2289
|
+
...stringValue(value.name) ? { name: stringValue(value.name) } : {},
|
|
2290
|
+
...stringValue(value.api) ? { api: stringValue(value.api) } : {},
|
|
2291
|
+
models
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
function sanitizeProviderModels(value) {
|
|
2295
|
+
if (!isRecord(value)) return {};
|
|
2296
|
+
const models = {};
|
|
2297
|
+
for (const [modelId, modelValue] of Object.entries(value)) {
|
|
2298
|
+
const model = sanitizeProviderModel(modelId, modelValue);
|
|
2299
|
+
if (model) models[modelId] = model;
|
|
2300
|
+
}
|
|
2301
|
+
return models;
|
|
2302
|
+
}
|
|
2303
|
+
function sanitizeProviderModel(modelId, value) {
|
|
2304
|
+
if (!isRecord(value)) return void 0;
|
|
2305
|
+
const reasoning = booleanValue(value.reasoning);
|
|
2306
|
+
const releaseDate = stringValue(value.release_date) ?? stringValue(value.releaseDate);
|
|
2307
|
+
const toolCall = booleanValue(value.tool_call) ?? booleanValue(value.toolCall);
|
|
2308
|
+
const limit = sanitizeLimit(value.limit);
|
|
2309
|
+
const modalities = sanitizeModalities(value.modalities);
|
|
2310
|
+
const variants = sanitizeVariants(value.variants);
|
|
2311
|
+
const model = {
|
|
2312
|
+
id: stringValue(value.id) ?? modelId,
|
|
2313
|
+
...stringValue(value.name) ? { name: stringValue(value.name) } : {},
|
|
2314
|
+
...stringValue(value.family) ? { family: stringValue(value.family) } : {},
|
|
2315
|
+
...releaseDate ? { releaseDate } : {},
|
|
2316
|
+
...booleanValue(value.attachment) !== void 0 ? { attachment: booleanValue(value.attachment) } : {},
|
|
2317
|
+
...reasoning !== void 0 ? { reasoning } : {},
|
|
2318
|
+
...booleanValue(value.temperature) !== void 0 ? { temperature: booleanValue(value.temperature) } : {},
|
|
2319
|
+
...toolCall !== void 0 ? { toolCall } : {},
|
|
2320
|
+
...stringValue(value.status) ? { status: stringValue(value.status) } : {},
|
|
2321
|
+
...limit ? { limit } : {},
|
|
2322
|
+
...modalities ? { modalities } : {},
|
|
2323
|
+
...variants ? { variants } : {}
|
|
2324
|
+
};
|
|
2325
|
+
const supportedReasoningEfforts = sanitizeReasoningEfforts(value.supportedReasoningEfforts) ?? sanitizeReasoningEfforts(value.supported_reasoning_efforts) ?? (reasoning ? allReasoningEfforts : void 0);
|
|
2326
|
+
if (supportedReasoningEfforts) model.supportedReasoningEfforts = supportedReasoningEfforts;
|
|
2327
|
+
return model;
|
|
2328
|
+
}
|
|
2329
|
+
function sanitizeLimit(value) {
|
|
2330
|
+
if (!isRecord(value)) return void 0;
|
|
2331
|
+
const limit = {
|
|
2332
|
+
...numberValue(value.context) !== void 0 ? { context: numberValue(value.context) } : {},
|
|
2333
|
+
...numberValue(value.input) !== void 0 ? { input: numberValue(value.input) } : {},
|
|
2334
|
+
...numberValue(value.output) !== void 0 ? { output: numberValue(value.output) } : {}
|
|
2335
|
+
};
|
|
2336
|
+
return Object.keys(limit).length ? limit : void 0;
|
|
2337
|
+
}
|
|
2338
|
+
function sanitizeModalities(value) {
|
|
2339
|
+
if (!isRecord(value)) return void 0;
|
|
2340
|
+
const modalities = {
|
|
2341
|
+
...stringArrayValue(value.input) ? { input: stringArrayValue(value.input) } : {},
|
|
2342
|
+
...stringArrayValue(value.output) ? { output: stringArrayValue(value.output) } : {}
|
|
2343
|
+
};
|
|
2344
|
+
return Object.keys(modalities).length ? modalities : void 0;
|
|
2345
|
+
}
|
|
2346
|
+
function sanitizeVariants(value) {
|
|
2347
|
+
if (!isRecord(value)) return void 0;
|
|
2348
|
+
const variants = {};
|
|
2349
|
+
for (const [variantId, variantValue] of Object.entries(value)) {
|
|
2350
|
+
variants[variantId] = isRecord(variantValue) && booleanValue(variantValue.disabled) !== void 0 ? { disabled: booleanValue(variantValue.disabled) } : {};
|
|
2351
|
+
}
|
|
2352
|
+
return Object.keys(variants).length ? variants : void 0;
|
|
2353
|
+
}
|
|
2354
|
+
function sanitizeReasoningEfforts(value) {
|
|
2355
|
+
const values = stringArrayValue(value);
|
|
2356
|
+
if (!values) return void 0;
|
|
2357
|
+
const efforts = values.filter((candidate) => allReasoningEfforts.includes(candidate));
|
|
2358
|
+
return efforts.length ? efforts : void 0;
|
|
2359
|
+
}
|
|
2360
|
+
function isRecord(value) {
|
|
2361
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2362
|
+
}
|
|
2363
|
+
function stringValue(value) {
|
|
2364
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
2365
|
+
}
|
|
2366
|
+
function numberValue(value) {
|
|
2367
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2368
|
+
}
|
|
2369
|
+
function booleanValue(value) {
|
|
2370
|
+
return typeof value === "boolean" ? value : void 0;
|
|
2371
|
+
}
|
|
2372
|
+
function stringArrayValue(value) {
|
|
2373
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
2374
|
+
}
|
|
2054
2375
|
async function executeToolInvocation(invocation, rootDir, streamOutput, timeoutMs) {
|
|
2055
2376
|
return new Promise((resolve, reject) => {
|
|
2056
2377
|
const child = spawn(invocation.command, invocation.args, {
|
|
@@ -2282,9 +2603,6 @@ function extractTextParts(parts) {
|
|
|
2282
2603
|
}
|
|
2283
2604
|
return parts.filter(isRecord).map((part) => part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("\n");
|
|
2284
2605
|
}
|
|
2285
|
-
function isRecord(value) {
|
|
2286
|
-
return typeof value === "object" && value !== null;
|
|
2287
|
-
}
|
|
2288
2606
|
function errorMessage(error) {
|
|
2289
2607
|
return error instanceof Error ? error.message : String(error);
|
|
2290
2608
|
}
|
|
@@ -2296,7 +2614,7 @@ function shellQuote(value) {
|
|
|
2296
2614
|
import { spawn as spawn2 } from "node:child_process";
|
|
2297
2615
|
import { createHash as createHash2 } from "node:crypto";
|
|
2298
2616
|
import { openSync } from "node:fs";
|
|
2299
|
-
import { mkdir as mkdir5, readdir as readdir2, readFile as
|
|
2617
|
+
import { mkdir as mkdir5, readdir as readdir2, readFile as readFile4, writeFile as writeFile5 } from "node:fs/promises";
|
|
2300
2618
|
import os3 from "node:os";
|
|
2301
2619
|
import path6 from "node:path";
|
|
2302
2620
|
function currentRunnerMode() {
|
|
@@ -2463,7 +2781,7 @@ function runnerDaemonKey(input) {
|
|
|
2463
2781
|
}
|
|
2464
2782
|
async function readRunnerDaemonMetadataFile(filePath) {
|
|
2465
2783
|
try {
|
|
2466
|
-
const parsed = JSON.parse(await
|
|
2784
|
+
const parsed = JSON.parse(await readFile4(filePath, "utf8"));
|
|
2467
2785
|
if (parsed.schemaVersion !== 1 || !parsed.runnerId || !parsed.projectId || !parsed.repositoryLinkId) {
|
|
2468
2786
|
return void 0;
|
|
2469
2787
|
}
|
|
@@ -2476,7 +2794,7 @@ async function readRunnerDaemonMetadataFile(filePath) {
|
|
|
2476
2794
|
// src/runner-service.ts
|
|
2477
2795
|
import { spawn as spawn3 } from "node:child_process";
|
|
2478
2796
|
import { createHash as createHash3 } from "node:crypto";
|
|
2479
|
-
import { mkdir as mkdir6, readFile as
|
|
2797
|
+
import { mkdir as mkdir6, readFile as readFile5, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
|
|
2480
2798
|
import os4 from "node:os";
|
|
2481
2799
|
import path7 from "node:path";
|
|
2482
2800
|
function detectRunnerServicePlatform(platform = process.platform) {
|
|
@@ -2542,7 +2860,7 @@ async function removeRunnerService(input) {
|
|
|
2542
2860
|
}
|
|
2543
2861
|
async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetadataDir()) {
|
|
2544
2862
|
try {
|
|
2545
|
-
const parsed = JSON.parse(await
|
|
2863
|
+
const parsed = JSON.parse(await readFile5(runnerServiceMetadataPath(input, metadataDir), "utf8"));
|
|
2546
2864
|
if (parsed.schemaVersion !== 1 || !parsed.serviceName || !parsed.serviceFilePath) {
|
|
2547
2865
|
return void 0;
|
|
2548
2866
|
}
|
|
@@ -2822,10 +3140,28 @@ function tokens(value) {
|
|
|
2822
3140
|
}
|
|
2823
3141
|
|
|
2824
3142
|
// src/sync.ts
|
|
2825
|
-
import {
|
|
3143
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
3144
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3145
|
+
import { mkdir as mkdir7, readdir as readdir3, readFile as readFile6, stat as stat3, writeFile as writeFile7 } from "node:fs/promises";
|
|
2826
3146
|
import path8 from "node:path";
|
|
3147
|
+
import { promisify as promisify3 } from "node:util";
|
|
3148
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
2827
3149
|
var legacySyncRoots = ["architecture", "context", "decisions", "features", "memory", "plans", "prompts", "workflows"];
|
|
2828
3150
|
var syncRoots = legacySyncRoots.map((syncRoot) => `docs/${syncRoot}`);
|
|
3151
|
+
var documentTypeByRoot = {
|
|
3152
|
+
architecture: "architecture",
|
|
3153
|
+
context: "context",
|
|
3154
|
+
decisions: "decision",
|
|
3155
|
+
features: "feature",
|
|
3156
|
+
memory: "memory",
|
|
3157
|
+
plans: "plan",
|
|
3158
|
+
prompts: "prompt",
|
|
3159
|
+
workflows: "workflow"
|
|
3160
|
+
};
|
|
3161
|
+
var autoSyncMetadataPaths = /* @__PURE__ */ new Set(["docs/context/amistio-project.md", "context/amistio-project.md"]);
|
|
3162
|
+
var autoSyncExcludedDirectoryNames = /* @__PURE__ */ new Set([".git", "node_modules", ".pnpm-store", ".next", "dist", "build", "coverage", ".cache", "cache", "tmp", "temp", "vendor"]);
|
|
3163
|
+
var autoSyncGeneratedPathSegments = /* @__PURE__ */ new Set(["generated", "__generated__", "vendor", "vendors"]);
|
|
3164
|
+
var defaultAutoSyncMaxFileKb = 256;
|
|
2829
3165
|
async function collectSyncStatus(rootDir, webDocuments = []) {
|
|
2830
3166
|
const localDocuments = await readLocalSyncedDocuments(rootDir);
|
|
2831
3167
|
const normalizedWebDocuments = webDocuments.map((document) => ({ ...document, repoPath: canonicalControlPlaneRepoPath(document.repoPath) }));
|
|
@@ -2902,7 +3238,7 @@ async function readLocalSyncedDocuments(rootDir) {
|
|
|
2902
3238
|
const markdownFiles = await findMarkdownFiles(rootDir);
|
|
2903
3239
|
const documents = [];
|
|
2904
3240
|
for (const fullPath of markdownFiles) {
|
|
2905
|
-
const raw = await
|
|
3241
|
+
const raw = await readFile6(fullPath, "utf8");
|
|
2906
3242
|
const parsed = parseSyncedMarkdown(raw);
|
|
2907
3243
|
if (!parsed) {
|
|
2908
3244
|
continue;
|
|
@@ -2985,6 +3321,29 @@ async function collectDirtyDocumentsForPush(rootDir, metadata) {
|
|
|
2985
3321
|
});
|
|
2986
3322
|
});
|
|
2987
3323
|
}
|
|
3324
|
+
async function collectAutoSyncDocumentsForPush(rootDir, metadata, existingDocuments = [], options = {}) {
|
|
3325
|
+
const dirtyDocuments = await collectDirtyDocumentsForPush(rootDir, metadata);
|
|
3326
|
+
const external = await collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options);
|
|
3327
|
+
return {
|
|
3328
|
+
documents: [...dirtyDocuments, ...external.documents],
|
|
3329
|
+
managedDocumentIds: dirtyDocuments.map((document) => document.documentId),
|
|
3330
|
+
skipped: external.skipped,
|
|
3331
|
+
counts: autoSyncSkipCounts(external.skipped)
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
function autoSyncSkipCounts(skipped) {
|
|
3335
|
+
return {
|
|
3336
|
+
metadata: skipped.filter((item) => item.reason === "metadata").length,
|
|
3337
|
+
template: skipped.filter((item) => item.reason === "template").length,
|
|
3338
|
+
unsupported: skipped.filter((item) => item.reason === "unsupported").length,
|
|
3339
|
+
tooLarge: skipped.filter((item) => item.reason === "tooLarge").length,
|
|
3340
|
+
alreadyManaged: skipped.filter((item) => item.reason === "alreadyManaged").length,
|
|
3341
|
+
unchanged: skipped.filter((item) => item.reason === "unchanged").length,
|
|
3342
|
+
conflicted: skipped.filter((item) => item.reason === "conflicted").length,
|
|
3343
|
+
excluded: skipped.filter((item) => item.reason === "excluded").length,
|
|
3344
|
+
unreadable: skipped.filter((item) => item.reason === "unreadable").length
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
2988
3347
|
function createSyncedDocumentMarkdown(document) {
|
|
2989
3348
|
return [
|
|
2990
3349
|
"---",
|
|
@@ -3014,7 +3373,7 @@ function parseSyncedMarkdown(content) {
|
|
|
3014
3373
|
}
|
|
3015
3374
|
async function readExistingSyncedDocument(fullPath) {
|
|
3016
3375
|
try {
|
|
3017
|
-
const raw = await
|
|
3376
|
+
const raw = await readFile6(fullPath, "utf8");
|
|
3018
3377
|
const parsed = parseSyncedMarkdown(raw);
|
|
3019
3378
|
if (!parsed) {
|
|
3020
3379
|
return { exists: true };
|
|
@@ -3091,6 +3450,145 @@ function inferTitle(content, repoPath) {
|
|
|
3091
3450
|
const heading = content.split("\n").find((line) => line.startsWith("# "))?.replace(/^#\s+/, "").trim();
|
|
3092
3451
|
return heading || path8.basename(repoPath, path8.extname(repoPath));
|
|
3093
3452
|
}
|
|
3453
|
+
async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options) {
|
|
3454
|
+
const root = path8.resolve(rootDir);
|
|
3455
|
+
const maxBytes = (options.maxFileKb ?? defaultAutoSyncMaxFileKb) * 1024;
|
|
3456
|
+
const syncedAt = options.syncedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3457
|
+
const existingById = new Map(existingDocuments.map((document) => [document.documentId, document]));
|
|
3458
|
+
const repoPaths = await listAutoSyncCandidatePaths(root);
|
|
3459
|
+
const documents = [];
|
|
3460
|
+
const skipped = [];
|
|
3461
|
+
for (const repoPath of repoPaths) {
|
|
3462
|
+
const normalizedRepoPath = normalizeRepoPath3(repoPath);
|
|
3463
|
+
const canonicalRepoPath = canonicalControlPlaneRepoPath(normalizedRepoPath);
|
|
3464
|
+
const skipReason = autoSyncPathSkipReason(normalizedRepoPath);
|
|
3465
|
+
if (skipReason) {
|
|
3466
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: skipReason });
|
|
3467
|
+
continue;
|
|
3468
|
+
}
|
|
3469
|
+
const fullPath = safeRepoPath(root, normalizedRepoPath);
|
|
3470
|
+
const fileStat = await stat3(fullPath).catch(() => void 0);
|
|
3471
|
+
if (!fileStat?.isFile()) {
|
|
3472
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
|
|
3473
|
+
continue;
|
|
3474
|
+
}
|
|
3475
|
+
if (fileStat.size > maxBytes) {
|
|
3476
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "tooLarge" });
|
|
3477
|
+
continue;
|
|
3478
|
+
}
|
|
3479
|
+
const content = await readFile6(fullPath, "utf8").catch(() => void 0);
|
|
3480
|
+
if (content === void 0) {
|
|
3481
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
|
|
3482
|
+
continue;
|
|
3483
|
+
}
|
|
3484
|
+
if (parseSyncedMarkdown(content)) {
|
|
3485
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "alreadyManaged" });
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3488
|
+
const documentType = documentTypeForRepoPath(canonicalRepoPath);
|
|
3489
|
+
if (!documentType) {
|
|
3490
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "unsupported" });
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
const contentHash = sha256ContentHash(content);
|
|
3494
|
+
const documentId = stableExternalDocumentId(metadata, canonicalRepoPath);
|
|
3495
|
+
const existing = existingById.get(documentId);
|
|
3496
|
+
if (existing?.syncState === "conflicted" || existing?.status === "conflicted") {
|
|
3497
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "conflicted" });
|
|
3498
|
+
continue;
|
|
3499
|
+
}
|
|
3500
|
+
const lastAutoSyncedHash = typeof existing?.frontmatter.autoSyncedSourceHash === "string" ? existing.frontmatter.autoSyncedSourceHash : void 0;
|
|
3501
|
+
if (existing?.contentHash === contentHash || lastAutoSyncedHash === contentHash) {
|
|
3502
|
+
skipped.push({ repoPath: normalizedRepoPath, reason: "unchanged" });
|
|
3503
|
+
continue;
|
|
3504
|
+
}
|
|
3505
|
+
const revision = existing ? existing.revision + 1 : 0;
|
|
3506
|
+
documents.push(brainDocumentItemSchema.parse({
|
|
3507
|
+
id: documentId,
|
|
3508
|
+
type: "brainDocument",
|
|
3509
|
+
schemaVersion: 1,
|
|
3510
|
+
accountId: metadata.amistioAccountId,
|
|
3511
|
+
projectId: metadata.amistioProjectId,
|
|
3512
|
+
documentId,
|
|
3513
|
+
documentType,
|
|
3514
|
+
title: inferTitle(content, canonicalRepoPath),
|
|
3515
|
+
status: "reviewing",
|
|
3516
|
+
repoPath: canonicalRepoPath,
|
|
3517
|
+
content,
|
|
3518
|
+
contentHash,
|
|
3519
|
+
frontmatter: {
|
|
3520
|
+
...existing?.frontmatter ?? {},
|
|
3521
|
+
externalBrainPath: normalizedRepoPath,
|
|
3522
|
+
autoSyncedByCommand: "amistio sync watch",
|
|
3523
|
+
autoSyncedAt: syncedAt,
|
|
3524
|
+
autoSyncedSourceHash: contentHash,
|
|
3525
|
+
...lastAutoSyncedHash ? { amistioContentHash: lastAutoSyncedHash } : {}
|
|
3526
|
+
},
|
|
3527
|
+
revision,
|
|
3528
|
+
source: "repo",
|
|
3529
|
+
syncState: "dirtyInRepo",
|
|
3530
|
+
createdAt: existing?.createdAt ?? syncedAt,
|
|
3531
|
+
updatedAt: syncedAt
|
|
3532
|
+
}));
|
|
3533
|
+
}
|
|
3534
|
+
return { documents, skipped };
|
|
3535
|
+
}
|
|
3536
|
+
async function listAutoSyncCandidatePaths(rootDir) {
|
|
3537
|
+
const gitFiles = await execFileAsync3("git", ["-C", rootDir, "ls-files", "--cached", "--others", "--exclude-standard"]).then(({ stdout }) => stdout).catch(() => void 0);
|
|
3538
|
+
if (gitFiles !== void 0) {
|
|
3539
|
+
return uniqueSortedRepoPaths(gitFiles.split("\n").map(normalizeRepoPath3).filter((repoPath) => repoPath && isRecognizedBrainRepoPath(repoPath)));
|
|
3540
|
+
}
|
|
3541
|
+
const files = [];
|
|
3542
|
+
for (const syncRoot of [...syncRoots, ...legacySyncRoots]) {
|
|
3543
|
+
const fullRoot = path8.join(rootDir, syncRoot);
|
|
3544
|
+
if (await exists2(fullRoot)) {
|
|
3545
|
+
await walkAutoSyncFiles(rootDir, fullRoot, files);
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
return uniqueSortedRepoPaths(files);
|
|
3549
|
+
}
|
|
3550
|
+
async function walkAutoSyncFiles(rootDir, directory, files) {
|
|
3551
|
+
for (const entry of await readdir3(directory, { withFileTypes: true }).catch(() => [])) {
|
|
3552
|
+
const fullPath = path8.join(directory, entry.name);
|
|
3553
|
+
const repoPath = normalizeRepoPath3(path8.relative(rootDir, fullPath));
|
|
3554
|
+
if (entry.isDirectory()) {
|
|
3555
|
+
if (!autoSyncExcludedDirectoryNames.has(entry.name)) {
|
|
3556
|
+
await walkAutoSyncFiles(rootDir, fullPath, files);
|
|
3557
|
+
}
|
|
3558
|
+
} else if (entry.isFile() && isRecognizedBrainRepoPath(repoPath)) {
|
|
3559
|
+
files.push(repoPath);
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
function autoSyncPathSkipReason(repoPath) {
|
|
3564
|
+
if (autoSyncMetadataPaths.has(repoPath)) return "metadata";
|
|
3565
|
+
if (isControlPlaneTemplateRepoPath(repoPath)) return "template";
|
|
3566
|
+
if (!/\.(md|mdx)$/i.test(repoPath)) return "unsupported";
|
|
3567
|
+
const basename = repoPath.split("/").at(-1)?.toLowerCase() ?? "";
|
|
3568
|
+
if (basename.startsWith(".") || basename.endsWith(".lock") || basename.includes(".env") || basename.includes("secret") || basename.includes("credential")) return "excluded";
|
|
3569
|
+
if (repoPath.split("/").some((segment) => autoSyncExcludedDirectoryNames.has(segment) || autoSyncGeneratedPathSegments.has(segment))) return "excluded";
|
|
3570
|
+
return void 0;
|
|
3571
|
+
}
|
|
3572
|
+
function isRecognizedBrainRepoPath(repoPath) {
|
|
3573
|
+
const normalized = normalizeRepoPath3(repoPath);
|
|
3574
|
+
const [firstSegment, secondSegment] = normalized.split("/");
|
|
3575
|
+
return Boolean(firstSegment === "docs" && secondSegment && legacySyncRoots.includes(secondSegment) || firstSegment && legacySyncRoots.includes(firstSegment));
|
|
3576
|
+
}
|
|
3577
|
+
function documentTypeForRepoPath(repoPath) {
|
|
3578
|
+
const normalized = normalizeRepoPath3(repoPath);
|
|
3579
|
+
const segments = normalized.split("/");
|
|
3580
|
+
const root = segments[0] === "docs" ? segments[1] : segments[0];
|
|
3581
|
+
return root && root in documentTypeByRoot ? documentTypeByRoot[root] : void 0;
|
|
3582
|
+
}
|
|
3583
|
+
function stableExternalDocumentId(metadata, repoPath) {
|
|
3584
|
+
return `doc_external_${createHash4("sha256").update(`${metadata.amistioAccountId}:${metadata.amistioProjectId}:${metadata.repositoryLinkId}:${repoPath}`).digest("hex").slice(0, 24)}`;
|
|
3585
|
+
}
|
|
3586
|
+
function normalizeRepoPath3(repoPath) {
|
|
3587
|
+
return repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
3588
|
+
}
|
|
3589
|
+
function uniqueSortedRepoPaths(repoPaths) {
|
|
3590
|
+
return [...new Set(repoPaths)].sort((first, second) => first.localeCompare(second));
|
|
3591
|
+
}
|
|
3094
3592
|
function parseFrontmatterFromSyncedDocument(frontmatter) {
|
|
3095
3593
|
return {
|
|
3096
3594
|
amistioDocumentId: frontmatter.amistioDocumentId,
|
|
@@ -3110,7 +3608,7 @@ async function exists2(filePath) {
|
|
|
3110
3608
|
}
|
|
3111
3609
|
|
|
3112
3610
|
// src/tool-session-store.ts
|
|
3113
|
-
import { mkdir as mkdir8, readFile as
|
|
3611
|
+
import { mkdir as mkdir8, readFile as readFile7, writeFile as writeFile8 } from "node:fs/promises";
|
|
3114
3612
|
import os5 from "node:os";
|
|
3115
3613
|
import path9 from "node:path";
|
|
3116
3614
|
var LocalToolSessionStore = class {
|
|
@@ -3131,7 +3629,7 @@ var LocalToolSessionStore = class {
|
|
|
3131
3629
|
}
|
|
3132
3630
|
async read() {
|
|
3133
3631
|
try {
|
|
3134
|
-
return JSON.parse(await
|
|
3632
|
+
return JSON.parse(await readFile7(this.filePath, "utf8"));
|
|
3135
3633
|
} catch {
|
|
3136
3634
|
return {};
|
|
3137
3635
|
}
|
|
@@ -3431,7 +3929,7 @@ function stripJsonFence(value) {
|
|
|
3431
3929
|
}
|
|
3432
3930
|
|
|
3433
3931
|
// src/runner-status.ts
|
|
3434
|
-
import { createHash as
|
|
3932
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
3435
3933
|
var watchStateReminderMs = 60 * 1e3;
|
|
3436
3934
|
function formatWatchStartupContext(input) {
|
|
3437
3935
|
return [
|
|
@@ -3452,7 +3950,7 @@ function watchStateKey(action) {
|
|
|
3452
3950
|
return [action.kind, action.message, action.workItemId, action.documentId, action.runnerId].filter(Boolean).join(":");
|
|
3453
3951
|
}
|
|
3454
3952
|
function stableRunnerId(input) {
|
|
3455
|
-
const digest =
|
|
3953
|
+
const digest = createHash5("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.machineId}`).digest("hex").slice(0, 20);
|
|
3456
3954
|
return `runner_${digest}`;
|
|
3457
3955
|
}
|
|
3458
3956
|
|
|
@@ -3576,12 +4074,12 @@ function roundNumber(value, digits) {
|
|
|
3576
4074
|
}
|
|
3577
4075
|
|
|
3578
4076
|
// src/importer.ts
|
|
3579
|
-
import { execFile as
|
|
3580
|
-
import { createHash as
|
|
3581
|
-
import { readdir as readdir4, readFile as
|
|
4077
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
4078
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
4079
|
+
import { readdir as readdir4, readFile as readFile8, stat as stat4 } from "node:fs/promises";
|
|
3582
4080
|
import path10 from "node:path";
|
|
3583
|
-
import { promisify as
|
|
3584
|
-
var
|
|
4081
|
+
import { promisify as promisify4 } from "node:util";
|
|
4082
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
3585
4083
|
var defaultMaxFileKb = 256;
|
|
3586
4084
|
var controlPlaneRoots2 = ["architecture", "context", "decisions", "features", "memory", "plans", "prompts", "workflows"];
|
|
3587
4085
|
var excludedDirectoryNames = /* @__PURE__ */ new Set([".git", "node_modules", ".pnpm-store", ".next", "dist", "build", "coverage", ".cache", "cache", "tmp", "temp", "vendor"]);
|
|
@@ -3644,7 +4142,7 @@ async function scanLegacyDocuments(options) {
|
|
|
3644
4142
|
skipped.push({ repoPath, reason: "tooLarge" });
|
|
3645
4143
|
continue;
|
|
3646
4144
|
}
|
|
3647
|
-
const content = await
|
|
4145
|
+
const content = await readFile8(fullPath, "utf8").catch(() => void 0);
|
|
3648
4146
|
if (content === void 0) {
|
|
3649
4147
|
skipped.push({ repoPath, reason: "unreadable" });
|
|
3650
4148
|
continue;
|
|
@@ -3725,7 +4223,7 @@ function parseOptionalOriginCloneUrl(originUrl) {
|
|
|
3725
4223
|
async function listRepositoryPaths(rootDir) {
|
|
3726
4224
|
const gitFiles = await runGit2(["-C", rootDir, "ls-files", "--cached", "--others", "--exclude-standard"]).catch(() => void 0);
|
|
3727
4225
|
if (gitFiles !== void 0) {
|
|
3728
|
-
return gitFiles.split("\n").map((line) =>
|
|
4226
|
+
return gitFiles.split("\n").map((line) => normalizeRepoPath4(line)).filter((line) => line.length > 0);
|
|
3729
4227
|
}
|
|
3730
4228
|
const files = [];
|
|
3731
4229
|
await walkRepository(rootDir, rootDir, files);
|
|
@@ -3735,7 +4233,7 @@ async function walkRepository(rootDir, directory, files) {
|
|
|
3735
4233
|
const entries = await readdir4(directory, { withFileTypes: true }).catch(() => []);
|
|
3736
4234
|
for (const entry of entries) {
|
|
3737
4235
|
const fullPath = path10.join(directory, entry.name);
|
|
3738
|
-
const repoPath =
|
|
4236
|
+
const repoPath = normalizeRepoPath4(path10.relative(rootDir, fullPath));
|
|
3739
4237
|
if (entry.isDirectory()) {
|
|
3740
4238
|
if (!excludedDirectoryNames.has(entry.name)) {
|
|
3741
4239
|
await walkRepository(rootDir, fullPath, files);
|
|
@@ -3755,7 +4253,7 @@ function matchesIncludeExclude(repoPath, include, exclude) {
|
|
|
3755
4253
|
return true;
|
|
3756
4254
|
}
|
|
3757
4255
|
function wildcardMatch(pattern, repoPath) {
|
|
3758
|
-
const normalizedPattern =
|
|
4256
|
+
const normalizedPattern = normalizeRepoPath4(pattern);
|
|
3759
4257
|
const escaped = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "::DOUBLE_STAR_SLASH::").replace(/\*\*/g, "::DOUBLE_STAR::").replace(/\?/g, "[^/]").replace(/\*/g, "[^/]*").replace(/::DOUBLE_STAR_SLASH::/g, "(?:.*/)?").replace(/::DOUBLE_STAR::/g, ".*");
|
|
3760
4258
|
return new RegExp(`^${escaped}$`).test(repoPath);
|
|
3761
4259
|
}
|
|
@@ -3808,7 +4306,7 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
|
|
|
3808
4306
|
return uniquePath;
|
|
3809
4307
|
}
|
|
3810
4308
|
function isCanonicalControlPlanePath(repoPath) {
|
|
3811
|
-
const [firstSegment, secondSegment] =
|
|
4309
|
+
const [firstSegment, secondSegment] = normalizeRepoPath4(repoPath).split("/");
|
|
3812
4310
|
return firstSegment === "docs" && Boolean(secondSegment && controlPlaneRoots2.includes(secondSegment));
|
|
3813
4311
|
}
|
|
3814
4312
|
function isLegacyControlPlanePath(repoPath) {
|
|
@@ -3841,13 +4339,13 @@ function stableImportDocumentId(accountId, projectId, repositoryLinkId, sourcePa
|
|
|
3841
4339
|
return `doc_import_${hashText(`${accountId}\0${projectId}\0${repositoryLinkId}\0${sourcePath}`, 24)}`;
|
|
3842
4340
|
}
|
|
3843
4341
|
function hashText(value, length) {
|
|
3844
|
-
return
|
|
4342
|
+
return createHash6("sha256").update(value).digest("hex").slice(0, length);
|
|
3845
4343
|
}
|
|
3846
|
-
function
|
|
4344
|
+
function normalizeRepoPath4(value) {
|
|
3847
4345
|
return value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
3848
4346
|
}
|
|
3849
4347
|
async function runGit2(args) {
|
|
3850
|
-
const { stdout } = await
|
|
4348
|
+
const { stdout } = await execFileAsync4("git", args, { maxBuffer: 10 * 1024 * 1024 });
|
|
3851
4349
|
return stdout.trim();
|
|
3852
4350
|
}
|
|
3853
4351
|
|
|
@@ -3881,6 +4379,19 @@ function buildBackgroundRunnerArgs(options) {
|
|
|
3881
4379
|
if (options.model) {
|
|
3882
4380
|
args.push("--model", options.model);
|
|
3883
4381
|
}
|
|
4382
|
+
const providerId = options.providerId ?? options.provider;
|
|
4383
|
+
if (providerId) {
|
|
4384
|
+
args.push("--provider", providerId);
|
|
4385
|
+
}
|
|
4386
|
+
if (options.modelId) {
|
|
4387
|
+
args.push("--model-id", options.modelId);
|
|
4388
|
+
}
|
|
4389
|
+
if (options.modelVariant) {
|
|
4390
|
+
args.push("--model-variant", options.modelVariant);
|
|
4391
|
+
}
|
|
4392
|
+
if (options.reasoningEffort) {
|
|
4393
|
+
args.push("--reasoning-effort", options.reasoningEffort);
|
|
4394
|
+
}
|
|
3884
4395
|
if (options.maxIterations !== void 0) {
|
|
3885
4396
|
args.push("--max-iterations", String(options.maxIterations));
|
|
3886
4397
|
}
|
|
@@ -3931,11 +4442,11 @@ function truncateProcessOutput(value) {
|
|
|
3931
4442
|
}
|
|
3932
4443
|
|
|
3933
4444
|
// src/git-worktree.ts
|
|
3934
|
-
import { execFile as
|
|
4445
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
3935
4446
|
import { mkdir as mkdir9, stat as stat5 } from "node:fs/promises";
|
|
3936
4447
|
import path12 from "node:path";
|
|
3937
|
-
import { promisify as
|
|
3938
|
-
var
|
|
4448
|
+
import { promisify as promisify5 } from "node:util";
|
|
4449
|
+
var execFileAsync5 = promisify5(execFile5);
|
|
3939
4450
|
function needsGitWorktreeIsolation(workItem) {
|
|
3940
4451
|
return (workItem.workKind ?? "implementation") === "implementation";
|
|
3941
4452
|
}
|
|
@@ -3996,11 +4507,11 @@ async function assertBaseRevision(repoRoot, baseRevision, currentHead) {
|
|
|
3996
4507
|
}
|
|
3997
4508
|
}
|
|
3998
4509
|
async function gitOutput(cwd, args) {
|
|
3999
|
-
const { stdout } = await
|
|
4510
|
+
const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
|
|
4000
4511
|
return stdout.trim();
|
|
4001
4512
|
}
|
|
4002
4513
|
async function gitCommandSucceeds(cwd, args) {
|
|
4003
|
-
return
|
|
4514
|
+
return execFileAsync5("git", args, { cwd }).then(() => true, () => false);
|
|
4004
4515
|
}
|
|
4005
4516
|
async function pathExists(value) {
|
|
4006
4517
|
return stat5(value).then(() => true, () => false);
|
|
@@ -4255,6 +4766,45 @@ sync.command("push").description("Push local brain changes to Amistio for review
|
|
|
4255
4766
|
await materializeBrainDocuments(options.root, documents, { allowDirtyOverwrite: true });
|
|
4256
4767
|
console.log(`Pushed ${documents.length} local document${documents.length === 1 ? "" : "s"} for web review.`);
|
|
4257
4768
|
});
|
|
4769
|
+
sync.command("watch").description("Watch repository brain folders and auto-sync eligible local changes").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable watcher runner ID").option("--interval-seconds <seconds>", "Polling interval", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-file-kb <kb>", "Maximum Markdown/MDX file size to auto-sync", parsePositiveInteger, 256).option("--once", "Run one auto-sync cycle and exit").action(async (options) => {
|
|
4770
|
+
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
4771
|
+
if (!context) {
|
|
4772
|
+
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
if (!context.token) {
|
|
4776
|
+
console.log("No local runner credential found. Run `amistio pair --pairing-code <code>` to store this machine credential.");
|
|
4777
|
+
process.exitCode = 1;
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
const runnerId = options.runnerId ?? stableRunnerId({
|
|
4781
|
+
accountId: context.metadata.amistioAccountId,
|
|
4782
|
+
projectId: context.metadata.amistioProjectId,
|
|
4783
|
+
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
4784
|
+
machineId: runnerMachineId()
|
|
4785
|
+
});
|
|
4786
|
+
console.log(`Auto-sync watcher: ${runnerId}`);
|
|
4787
|
+
console.log(`Project: ${context.metadata.amistioProjectId}`);
|
|
4788
|
+
console.log(`Repository link: ${context.metadata.repositoryLinkId}`);
|
|
4789
|
+
let iterations = 0;
|
|
4790
|
+
while (true) {
|
|
4791
|
+
iterations += 1;
|
|
4792
|
+
const result = await runAutoSyncCycle({
|
|
4793
|
+
context,
|
|
4794
|
+
maxFileKb: options.maxFileKb,
|
|
4795
|
+
quietDisabled: false,
|
|
4796
|
+
root: options.root,
|
|
4797
|
+
runnerId
|
|
4798
|
+
});
|
|
4799
|
+
console.log(formatAutoSyncCycleResult(result));
|
|
4800
|
+
if (options.once || result.status === "disabled") return;
|
|
4801
|
+
if (options.maxIterations !== void 0 && iterations >= options.maxIterations) {
|
|
4802
|
+
console.log(`Auto-sync watcher stopped after ${iterations} polling attempt${iterations === 1 ? "" : "s"}.`);
|
|
4803
|
+
return;
|
|
4804
|
+
}
|
|
4805
|
+
await delay(options.intervalSeconds * 1e3);
|
|
4806
|
+
}
|
|
4807
|
+
});
|
|
4258
4808
|
var work = program.command("work").description("Inspect approved work items");
|
|
4259
4809
|
work.command("list").description("List queued work without claiming it").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
|
|
4260
4810
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
@@ -4311,7 +4861,7 @@ program.command("tools").description("List local AI coding tools that the Amisti
|
|
|
4311
4861
|
}
|
|
4312
4862
|
console.log("custom - pass --tool-command to use any other local runner command.");
|
|
4313
4863
|
});
|
|
4314
|
-
program.command("orchestrate").description("Update the Amistio control plane through a user-installed local AI tool").argument("[goal...]", "Goal or next-step instruction for the orchestration pass").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent", "auto").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel, "auto").option("--model <model>", "Model to request when the selected local tool supports model selection").option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--prompt-out <path>", "Write the generated orchestration prompt to a file before running").option("--dry-run", "Print the generated orchestration prompt without running a tool").option("--no-stream", "Capture local tool output instead of streaming it").action(async (goalParts, options) => {
|
|
4864
|
+
program.command("orchestrate").description("Update the Amistio control plane through a user-installed local AI tool").argument("[goal...]", "Goal or next-step instruction for the orchestration pass").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent", "auto").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel, "auto").option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--prompt-out <path>", "Write the generated orchestration prompt to a file before running").option("--dry-run", "Print the generated orchestration prompt without running a tool").option("--no-stream", "Capture local tool output instead of streaming it").action(async (goalParts, options) => {
|
|
4315
4865
|
const goal = goalParts?.join(" ").trim() || "Review the current repository state and update the Amistio control plane with the next useful orchestration steps.";
|
|
4316
4866
|
const prompt = await createOrchestrationPrompt({ rootDir: options.root, goal });
|
|
4317
4867
|
if (options.promptOut) {
|
|
@@ -4323,7 +4873,8 @@ program.command("orchestrate").description("Update the Amistio control plane thr
|
|
|
4323
4873
|
return;
|
|
4324
4874
|
}
|
|
4325
4875
|
const sessionPolicy = normalizeSessionPolicy(options.session);
|
|
4326
|
-
const
|
|
4876
|
+
const localModelConfig = localModelConfigOptions(options);
|
|
4877
|
+
const preview = await createToolRunPreview({ rootDir: options.root, prompt, tool: options.tool, invocationChannel: options.invocationChannel, ...options.toolCommand ? { toolCommand: options.toolCommand } : {}, ...localModelConfig });
|
|
4327
4878
|
console.log(`Running ${preview.toolName}: ${preview.displayCommand}`);
|
|
4328
4879
|
const result = await runLocalTool({
|
|
4329
4880
|
rootDir: options.root,
|
|
@@ -4331,7 +4882,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
|
|
|
4331
4882
|
tool: options.tool,
|
|
4332
4883
|
invocationChannel: options.invocationChannel,
|
|
4333
4884
|
...options.toolCommand ? { toolCommand: options.toolCommand } : {},
|
|
4334
|
-
...
|
|
4885
|
+
...localModelConfig,
|
|
4335
4886
|
streamOutput: options.stream,
|
|
4336
4887
|
...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
|
|
4337
4888
|
});
|
|
@@ -4345,7 +4896,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
|
|
|
4345
4896
|
process.exitCode = result.exitCode;
|
|
4346
4897
|
}
|
|
4347
4898
|
});
|
|
4348
|
-
program.command("run").description("Claim and run approved Amistio work locally").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--runner-id <runnerId>", "Stable runner ID").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--dry-run", "Claim work and print the generated execution prompt without running a tool").option("--watch", "Keep polling for approved work until stopped").option("--background", "Start a detached background runner that watches for approved work").option("--interval-seconds <seconds>", "Polling interval for --watch", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors while watching").action(async (options, command) => {
|
|
4899
|
+
program.command("run").description("Claim and run approved Amistio work locally").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--runner-id <runnerId>", "Stable runner ID").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--dry-run", "Claim work and print the generated execution prompt without running a tool").option("--watch", "Keep polling for approved work until stopped").option("--background", "Start a detached background runner that watches for approved work").option("--interval-seconds <seconds>", "Polling interval for --watch", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors while watching").action(async (options, command) => {
|
|
4349
4900
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
4350
4901
|
if (!context) {
|
|
4351
4902
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
@@ -4517,7 +5068,7 @@ runner.command("stop").description("Stop a background runner for the paired repo
|
|
|
4517
5068
|
console.log(stopResult === "stopped" ? `Stopped background runner ${record.runnerId}.` : `Marked background runner ${record.runnerId} stopped; process was not running.`);
|
|
4518
5069
|
});
|
|
4519
5070
|
var runnerService = runner.command("service").description("Manage a user-level startup service for the paired runner");
|
|
4520
|
-
runnerService.command("install").description("Install a user-level startup service for this paired repository runner").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable runner ID").option("--tool <name>", "Local tool to use: auto, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--interval-seconds <seconds>", "Polling interval for the service runner", parsePositiveInteger, 10).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors").option("--dry-run", "Print the startup service descriptor without installing it").action(async (options) => {
|
|
5071
|
+
runnerService.command("install").description("Install a user-level startup service for this paired repository runner").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable runner ID").option("--tool <name>", "Local tool to use: auto, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--interval-seconds <seconds>", "Polling interval for the service runner", parsePositiveInteger, 10).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors").option("--dry-run", "Print the startup service descriptor without installing it").action(async (options) => {
|
|
4521
5072
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
4522
5073
|
if (!context) {
|
|
4523
5074
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
@@ -4607,6 +5158,16 @@ runnerService.command("remove").description("Remove the startup service for this
|
|
|
4607
5158
|
});
|
|
4608
5159
|
async function runWatchIteration({ command, context, options, runnerId }) {
|
|
4609
5160
|
try {
|
|
5161
|
+
if (options.watch && !options.dryRun) {
|
|
5162
|
+
await runAutoSyncCycle({
|
|
5163
|
+
context,
|
|
5164
|
+
maxFileKb: 256,
|
|
5165
|
+
quiet: true,
|
|
5166
|
+
quietDisabled: true,
|
|
5167
|
+
root: options.root,
|
|
5168
|
+
runnerId
|
|
5169
|
+
});
|
|
5170
|
+
}
|
|
4610
5171
|
return await runNextWorkItem({
|
|
4611
5172
|
apiClient: context.client,
|
|
4612
5173
|
projectId: context.metadata.amistioProjectId,
|
|
@@ -4617,6 +5178,10 @@ async function runWatchIteration({ command, context, options, runnerId }) {
|
|
|
4617
5178
|
...command.getOptionValueSource("tool") === "cli" && options.tool ? { explicitTool: options.tool } : {},
|
|
4618
5179
|
...command.getOptionValueSource("invocationChannel") === "cli" && options.invocationChannel ? { explicitInvocationChannel: options.invocationChannel } : {},
|
|
4619
5180
|
...command.getOptionValueSource("model") === "cli" && options.model ? { explicitModel: options.model } : {},
|
|
5181
|
+
...command.getOptionValueSource("provider") === "cli" && options.provider ? { explicitProviderId: options.provider } : {},
|
|
5182
|
+
...command.getOptionValueSource("modelId") === "cli" && options.modelId ? { explicitModelId: options.modelId } : {},
|
|
5183
|
+
...command.getOptionValueSource("modelVariant") === "cli" && options.modelVariant ? { explicitModelVariant: options.modelVariant } : {},
|
|
5184
|
+
...command.getOptionValueSource("reasoningEffort") === "cli" && options.reasoningEffort ? { explicitReasoningEffort: options.reasoningEffort } : {},
|
|
4620
5185
|
...options.toolCommand ? { toolCommand: options.toolCommand } : {},
|
|
4621
5186
|
dryRun: Boolean(options.dryRun),
|
|
4622
5187
|
stream: options.stream,
|
|
@@ -4661,6 +5226,64 @@ ${detail}`);
|
|
|
4661
5226
|
return { status: "failed", exitCode: 1, message };
|
|
4662
5227
|
}
|
|
4663
5228
|
}
|
|
5229
|
+
async function runAutoSyncCycle({ context, maxFileKb, quiet, quietDisabled, root, runnerId }) {
|
|
5230
|
+
const projectId = context.metadata.amistioProjectId;
|
|
5231
|
+
const repositoryLinkId = context.metadata.repositoryLinkId;
|
|
5232
|
+
const heartbeatBase = runnerHeartbeatMetadata(void 0);
|
|
5233
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5234
|
+
try {
|
|
5235
|
+
const { repositoryLinks } = await context.client.listRepositoryLinks(projectId);
|
|
5236
|
+
const repositoryLink = repositoryLinks.find((link) => link.repositoryLinkId === repositoryLinkId && link.status !== "revoked");
|
|
5237
|
+
if (!repositoryLink) {
|
|
5238
|
+
const message2 = "Repository link is not active for auto-sync.";
|
|
5239
|
+
await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "blocked", { ...heartbeatBase, autoSyncStatus: "blocked", autoSyncMessage: message2, autoSyncLastFailureAt: startedAt }).catch(() => void 0);
|
|
5240
|
+
return { status: "blocked", message: message2, pushedCount: 0, skippedCount: 0, conflictCount: 0 };
|
|
5241
|
+
}
|
|
5242
|
+
if (!repositoryLink.autoSyncEnabled) {
|
|
5243
|
+
const message2 = "Repository brain auto-sync is disabled.";
|
|
5244
|
+
if (!quietDisabled) {
|
|
5245
|
+
await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", { ...heartbeatBase, autoSyncStatus: "disabled", autoSyncMessage: message2 }).catch(() => void 0);
|
|
5246
|
+
}
|
|
5247
|
+
return { status: "disabled", message: message2, pushedCount: 0, skippedCount: 0, conflictCount: 0 };
|
|
5248
|
+
}
|
|
5249
|
+
await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", { ...heartbeatBase, autoSyncStatus: "active", autoSyncMessage: "Scanning repository brain folders.", autoSyncLastStartedAt: startedAt }).catch(() => void 0);
|
|
5250
|
+
const { documents: existingDocuments } = await context.client.listBrainDocuments(projectId);
|
|
5251
|
+
const collection = await collectAutoSyncDocumentsForPush(root, context.metadata, existingDocuments, { maxFileKb, syncedAt: startedAt });
|
|
5252
|
+
if (!collection.documents.length) {
|
|
5253
|
+
const skippedCount2 = collection.skipped.length;
|
|
5254
|
+
const message2 = skippedCount2 ? `No local brain changes pushed; ${skippedCount2} file${skippedCount2 === 1 ? "" : "s"} skipped or unchanged.` : "No local brain changes found.";
|
|
5255
|
+
await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", { ...heartbeatBase, autoSyncStatus: "synced", autoSyncMessage: message2, autoSyncLastSuccessAt: startedAt, autoSyncPushedCount: 0, autoSyncSkippedCount: skippedCount2, autoSyncConflictCount: 0 }).catch(() => void 0);
|
|
5256
|
+
return { status: "synced", message: message2, pushedCount: 0, skippedCount: skippedCount2, conflictCount: 0, collection };
|
|
5257
|
+
}
|
|
5258
|
+
const { documents } = await context.client.pushBrainDocuments(projectId, collection.documents);
|
|
5259
|
+
const conflictedDocuments = documents.filter((document) => document.syncState === "conflicted" || document.status === "conflicted");
|
|
5260
|
+
const managedDocumentIds = new Set(collection.managedDocumentIds);
|
|
5261
|
+
const managedDocuments = documents.filter((document) => managedDocumentIds.has(document.documentId) && document.syncState !== "conflicted" && document.status !== "conflicted");
|
|
5262
|
+
if (managedDocuments.length) {
|
|
5263
|
+
await materializeBrainDocuments(root, managedDocuments, { allowDirtyOverwrite: true });
|
|
5264
|
+
}
|
|
5265
|
+
const conflictCount = conflictedDocuments.length;
|
|
5266
|
+
const skippedCount = collection.skipped.length;
|
|
5267
|
+
const pushedCount = documents.length - conflictCount;
|
|
5268
|
+
const status = conflictCount ? "conflicted" : "synced";
|
|
5269
|
+
const message = conflictCount ? `Auto-sync found ${conflictCount} conflict${conflictCount === 1 ? "" : "s"}.` : `Auto-sync pushed ${pushedCount} brain document${pushedCount === 1 ? "" : "s"}.`;
|
|
5270
|
+
await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", { ...heartbeatBase, autoSyncStatus: status, autoSyncMessage: message, ...conflictCount ? { autoSyncLastFailureAt: startedAt } : { autoSyncLastSuccessAt: startedAt }, autoSyncPushedCount: pushedCount, autoSyncSkippedCount: skippedCount, autoSyncConflictCount: conflictCount }).catch(() => void 0);
|
|
5271
|
+
if (!quiet && conflictCount) {
|
|
5272
|
+
for (const document of conflictedDocuments) {
|
|
5273
|
+
console.log(`conflicted: ${document.repoPath} - web and repository revisions both changed`);
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
return { status, message, pushedCount, skippedCount, conflictCount, collection };
|
|
5277
|
+
} catch (error) {
|
|
5278
|
+
const message = `Auto-sync failed: ${errorMessage3(error)}`;
|
|
5279
|
+
await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "blocked", { ...heartbeatBase, autoSyncStatus: "failed", autoSyncMessage: message, autoSyncLastFailureAt: startedAt }).catch(() => void 0);
|
|
5280
|
+
return { status: "failed", message, pushedCount: 0, skippedCount: 0, conflictCount: 0 };
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
function formatAutoSyncCycleResult(result) {
|
|
5284
|
+
const details = [`pushed ${result.pushedCount}`, `skipped ${result.skippedCount}`, `conflicts ${result.conflictCount}`];
|
|
5285
|
+
return `${result.message} (${details.join("; ")})`;
|
|
5286
|
+
}
|
|
4664
5287
|
async function runNextWorkItem({
|
|
4665
5288
|
apiClient,
|
|
4666
5289
|
dryRun,
|
|
@@ -4671,6 +5294,10 @@ async function runNextWorkItem({
|
|
|
4671
5294
|
sessionPolicy,
|
|
4672
5295
|
stream,
|
|
4673
5296
|
explicitModel,
|
|
5297
|
+
explicitModelId,
|
|
5298
|
+
explicitModelVariant,
|
|
5299
|
+
explicitProviderId,
|
|
5300
|
+
explicitReasoningEffort,
|
|
4674
5301
|
explicitInvocationChannel,
|
|
4675
5302
|
explicitTool,
|
|
4676
5303
|
maxPreflightAttempts,
|
|
@@ -4685,6 +5312,10 @@ async function runNextWorkItem({
|
|
|
4685
5312
|
projectId,
|
|
4686
5313
|
...explicitInvocationChannel ? { explicitInvocationChannel } : {},
|
|
4687
5314
|
...explicitModel ? { explicitModel } : {},
|
|
5315
|
+
...explicitProviderId ? { explicitProviderId } : {},
|
|
5316
|
+
...explicitModelId ? { explicitModelId } : {},
|
|
5317
|
+
...explicitModelVariant ? { explicitModelVariant } : {},
|
|
5318
|
+
...explicitReasoningEffort ? { explicitReasoningEffort } : {},
|
|
4688
5319
|
...explicitTool ? { explicitTool } : {},
|
|
4689
5320
|
...toolCommand ? { toolCommand } : {}
|
|
4690
5321
|
});
|
|
@@ -4734,7 +5365,8 @@ async function runNextWorkItem({
|
|
|
4734
5365
|
...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
|
|
4735
5366
|
...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
|
|
4736
5367
|
});
|
|
4737
|
-
const
|
|
5368
|
+
const resolvedModelConfig = toolConfigModelOptions(toolConfig);
|
|
5369
|
+
const preview = await createToolRunPreview({ rootDir: executionRoot, prompt, tool: toolConfig.tool, invocationChannel: toolConfig.requestedInvocationChannel ?? "auto", ...toolCommand ? { toolCommand } : {}, ...resolvedModelConfig });
|
|
4738
5370
|
const sessionContext = await prepareToolSession({
|
|
4739
5371
|
apiClient,
|
|
4740
5372
|
projectId,
|
|
@@ -4768,7 +5400,7 @@ async function runNextWorkItem({
|
|
|
4768
5400
|
tool: toolConfig.tool,
|
|
4769
5401
|
invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
|
|
4770
5402
|
...toolCommand ? { toolCommand } : {},
|
|
4771
|
-
...
|
|
5403
|
+
...resolvedModelConfig,
|
|
4772
5404
|
streamOutput: stream,
|
|
4773
5405
|
timeoutMs: toolTimeoutMs,
|
|
4774
5406
|
...sessionContext.toolSession ? {
|
|
@@ -5656,11 +6288,17 @@ function parseInvocationChannel(value) {
|
|
|
5656
6288
|
}
|
|
5657
6289
|
throw new Error(`Expected invocation channel auto, sdk, or command; received ${value}.`);
|
|
5658
6290
|
}
|
|
6291
|
+
function parseReasoningEffort(value) {
|
|
6292
|
+
if (value === "auto" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
|
|
6293
|
+
return value;
|
|
6294
|
+
}
|
|
6295
|
+
throw new Error(`Expected reasoning effort auto, low, medium, high, or xhigh; received ${value}.`);
|
|
6296
|
+
}
|
|
5659
6297
|
function inferRepoName(root) {
|
|
5660
6298
|
return path13.basename(path13.resolve(root)) || "repository";
|
|
5661
6299
|
}
|
|
5662
6300
|
function createRepoFingerprint(accountId, projectId, repositoryLinkId) {
|
|
5663
|
-
return
|
|
6301
|
+
return createHash7("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");
|
|
5664
6302
|
}
|
|
5665
6303
|
function defaultApiUrl() {
|
|
5666
6304
|
const envApiUrl = process.env[AMISTIO_API_URL_ENV]?.trim();
|
|
@@ -5675,9 +6313,10 @@ function formatApiUrlFlag(apiUrl) {
|
|
|
5675
6313
|
function formatShellArg(value) {
|
|
5676
6314
|
return /^[A-Za-z0-9_./:@-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
5677
6315
|
}
|
|
5678
|
-
async function resolveRunnerToolConfig({ apiClient, explicitInvocationChannel, explicitModel, explicitTool, projectId, toolCommand }) {
|
|
6316
|
+
async function resolveRunnerToolConfig({ apiClient, explicitInvocationChannel, explicitModel, explicitModelId, explicitModelVariant, explicitProviderId, explicitReasoningEffort, explicitTool, projectId, toolCommand }) {
|
|
5679
6317
|
const capabilities = toRunnerToolCapabilities(await detectLocalTools());
|
|
5680
6318
|
if (toolCommand) {
|
|
6319
|
+
const modelConfig2 = normalizeModelConfig({ model: explicitModel, providerId: explicitProviderId, modelId: explicitModelId, modelVariant: explicitModelVariant, reasoningEffort: explicitReasoningEffort });
|
|
5681
6320
|
return {
|
|
5682
6321
|
ready: true,
|
|
5683
6322
|
tool: explicitTool ?? "auto",
|
|
@@ -5688,33 +6327,42 @@ async function resolveRunnerToolConfig({ apiClient, explicitInvocationChannel, e
|
|
|
5688
6327
|
requestedInvocationChannel: explicitInvocationChannel ?? "command",
|
|
5689
6328
|
effectiveInvocationChannel: "command",
|
|
5690
6329
|
...explicitTool && explicitTool !== "none" && explicitTool !== "auto" && isLocalToolName(explicitTool) ? { requestedTool: explicitTool } : explicitTool === "auto" ? { requestedTool: "auto" } : {},
|
|
5691
|
-
...
|
|
6330
|
+
...modelConfig2,
|
|
5692
6331
|
message: "Using local custom tool command."
|
|
5693
6332
|
};
|
|
5694
6333
|
}
|
|
5695
6334
|
if (explicitTool === "none") {
|
|
5696
|
-
|
|
5697
|
-
|
|
6335
|
+
const modelConfig2 = normalizeModelConfig({ model: explicitModel, providerId: explicitProviderId, modelId: explicitModelId, modelVariant: explicitModelVariant, reasoningEffort: explicitReasoningEffort });
|
|
6336
|
+
if (hasModelConfig(modelConfig2)) {
|
|
6337
|
+
return unavailableToolConfig({ capabilities, source: "cli", status: "modelUnsupported", message: "Model configuration cannot be used with --tool none.", tool: "none", requestedInvocationChannel: explicitInvocationChannel ?? "auto", ...modelConfig2 });
|
|
5698
6338
|
}
|
|
5699
6339
|
return { ready: true, tool: "none", capabilities, source: "cli", status: "none", requestedInvocationChannel: explicitInvocationChannel ?? "auto", message: "No local tool selected." };
|
|
5700
6340
|
}
|
|
5701
6341
|
if (explicitTool && explicitTool !== "auto" && !isLocalToolName(explicitTool)) {
|
|
5702
|
-
|
|
6342
|
+
const modelConfig2 = normalizeModelConfig({ model: explicitModel, providerId: explicitProviderId, modelId: explicitModelId, modelVariant: explicitModelVariant, reasoningEffort: explicitReasoningEffort });
|
|
6343
|
+
return unavailableToolConfig({ capabilities, source: "cli", status: "unavailable", message: `Unsupported local tool: ${explicitTool}.`, tool: explicitTool, requestedInvocationChannel: explicitInvocationChannel ?? "auto", ...modelConfig2 });
|
|
5703
6344
|
}
|
|
5704
6345
|
const remotePreference = await apiClient.getRunnerPreferences(projectId).then((response) => response.effective).catch(() => void 0);
|
|
5705
6346
|
const requestedTool = explicitTool ?? remotePreference?.tool ?? "auto";
|
|
5706
6347
|
const requestedInvocationChannel = explicitInvocationChannel ?? remotePreference?.invocationChannel ?? "auto";
|
|
5707
|
-
const
|
|
5708
|
-
|
|
5709
|
-
|
|
6348
|
+
const modelConfig = normalizeModelConfig({
|
|
6349
|
+
model: explicitModel ?? remotePreference?.model,
|
|
6350
|
+
providerId: explicitProviderId ?? remotePreference?.providerId,
|
|
6351
|
+
modelId: explicitModelId ?? remotePreference?.modelId,
|
|
6352
|
+
modelVariant: explicitModelVariant ?? remotePreference?.modelVariant,
|
|
6353
|
+
reasoningEffort: explicitReasoningEffort ?? remotePreference?.reasoningEffort
|
|
6354
|
+
});
|
|
6355
|
+
const source = explicitTool || explicitInvocationChannel || hasExplicitModelConfig({ model: explicitModel, providerId: explicitProviderId, modelId: explicitModelId, modelVariant: explicitModelVariant, reasoningEffort: explicitReasoningEffort }) ? "cli" : remotePreference?.source ?? "default";
|
|
6356
|
+
return resolveRequestedTool({ capabilities, modelConfig, requestedInvocationChannel, requestedTool, source });
|
|
5710
6357
|
}
|
|
5711
|
-
function resolveRequestedTool({ capabilities,
|
|
6358
|
+
function resolveRequestedTool({ capabilities, modelConfig, requestedInvocationChannel, requestedTool, source }) {
|
|
6359
|
+
const needsModelSelection = hasModelConfig(modelConfig);
|
|
5712
6360
|
if (requestedTool === "auto") {
|
|
5713
|
-
const candidate = capabilities.find((capability2) => capability2.available && capabilitySupportsInvocationChannel(capability2, requestedInvocationChannel) && (!
|
|
6361
|
+
const candidate = capabilities.find((capability2) => capability2.available && capabilitySupportsInvocationChannel(capability2, requestedInvocationChannel) && (!needsModelSelection || capability2.supportsModelSelection));
|
|
5714
6362
|
if (!candidate) {
|
|
5715
6363
|
const anyAvailable = capabilities.some((capability2) => capability2.available);
|
|
5716
6364
|
const anyChannelAvailable = capabilities.some((capability2) => capability2.available && capabilitySupportsInvocationChannel(capability2, requestedInvocationChannel));
|
|
5717
|
-
const status = !anyAvailable ? "unavailable" : requestedInvocationChannel !== "auto" && !anyChannelAvailable ? "channelUnsupported" :
|
|
6365
|
+
const status = !anyAvailable ? "unavailable" : requestedInvocationChannel !== "auto" && !anyChannelAvailable ? "channelUnsupported" : needsModelSelection ? "modelUnsupported" : "unavailable";
|
|
5718
6366
|
return unavailableToolConfig({
|
|
5719
6367
|
capabilities,
|
|
5720
6368
|
source,
|
|
@@ -5722,23 +6370,31 @@ function resolveRequestedTool({ capabilities, model, requestedInvocationChannel,
|
|
|
5722
6370
|
requestedTool,
|
|
5723
6371
|
requestedInvocationChannel,
|
|
5724
6372
|
tool: "auto",
|
|
5725
|
-
...
|
|
5726
|
-
message: status === "channelUnsupported" ? `No installed local AI tool can honor ${requestedInvocationChannel} invocation.` :
|
|
6373
|
+
...modelConfig,
|
|
6374
|
+
message: status === "channelUnsupported" ? `No installed local AI tool can honor ${requestedInvocationChannel} invocation.` : needsModelSelection ? "No installed local tool can honor the selected provider/model configuration." : "No supported local AI tool is installed."
|
|
5727
6375
|
});
|
|
5728
6376
|
}
|
|
5729
|
-
|
|
6377
|
+
const modelResolution2 = resolveProviderModelConfig(candidate, modelConfig);
|
|
6378
|
+
if (!modelResolution2.ready) {
|
|
6379
|
+
return unavailableToolConfig({ capabilities, source, status: modelResolution2.status, requestedTool, requestedInvocationChannel, effectiveTool: candidate.name, effectiveInvocationChannel: effectiveInvocationChannel(candidate, requestedInvocationChannel), tool: "auto", ...modelConfig, message: modelResolution2.message });
|
|
6380
|
+
}
|
|
6381
|
+
return { ready: true, tool: "auto", capabilities, source, status: "resolved", requestedTool, requestedInvocationChannel, effectiveTool: candidate.name, effectiveInvocationChannel: effectiveInvocationChannel(candidate, requestedInvocationChannel), ...modelResolution2.config };
|
|
5730
6382
|
}
|
|
5731
6383
|
const capability = capabilities.find((candidate) => candidate.name === requestedTool);
|
|
5732
6384
|
if (!capability?.available) {
|
|
5733
|
-
return unavailableToolConfig({ capabilities, source, status: "unavailable", requestedTool, requestedInvocationChannel, tool: requestedTool, ...
|
|
6385
|
+
return unavailableToolConfig({ capabilities, source, status: "unavailable", requestedTool, requestedInvocationChannel, tool: requestedTool, ...modelConfig, message: `${requestedTool} is selected but is not available on this runner.` });
|
|
5734
6386
|
}
|
|
5735
6387
|
if (!capabilitySupportsInvocationChannel(capability, requestedInvocationChannel)) {
|
|
5736
|
-
return unavailableToolConfig({ capabilities, source, status: "channelUnsupported", requestedTool, requestedInvocationChannel, effectiveTool: requestedTool, tool: requestedTool, ...
|
|
6388
|
+
return unavailableToolConfig({ capabilities, source, status: "channelUnsupported", requestedTool, requestedInvocationChannel, effectiveTool: requestedTool, tool: requestedTool, ...modelConfig, message: `${requestedTool} is available but does not support ${requestedInvocationChannel} invocation on this runner.` });
|
|
6389
|
+
}
|
|
6390
|
+
if (needsModelSelection && !capability.supportsModelSelection) {
|
|
6391
|
+
return unavailableToolConfig({ capabilities, source, status: "modelUnsupported", requestedTool, requestedInvocationChannel, effectiveTool: requestedTool, effectiveInvocationChannel: effectiveInvocationChannel(capability, requestedInvocationChannel), tool: requestedTool, ...modelConfig, message: `${requestedTool} is available but does not support Amistio model selection yet.` });
|
|
5737
6392
|
}
|
|
5738
|
-
|
|
5739
|
-
|
|
6393
|
+
const modelResolution = resolveProviderModelConfig(capability, modelConfig);
|
|
6394
|
+
if (!modelResolution.ready) {
|
|
6395
|
+
return unavailableToolConfig({ capabilities, source, status: modelResolution.status, requestedTool, requestedInvocationChannel, effectiveTool: requestedTool, effectiveInvocationChannel: effectiveInvocationChannel(capability, requestedInvocationChannel), tool: requestedTool, ...modelConfig, message: modelResolution.message });
|
|
5740
6396
|
}
|
|
5741
|
-
return { ready: true, tool: requestedTool, capabilities, source, status: "resolved", requestedTool, requestedInvocationChannel, effectiveTool: requestedTool, effectiveInvocationChannel: effectiveInvocationChannel(capability, requestedInvocationChannel), ...
|
|
6397
|
+
return { ready: true, tool: requestedTool, capabilities, source, status: "resolved", requestedTool, requestedInvocationChannel, effectiveTool: requestedTool, effectiveInvocationChannel: effectiveInvocationChannel(capability, requestedInvocationChannel), ...modelResolution.config };
|
|
5742
6398
|
}
|
|
5743
6399
|
function unavailableToolConfig(input) {
|
|
5744
6400
|
return {
|
|
@@ -5752,9 +6408,116 @@ function unavailableToolConfig(input) {
|
|
|
5752
6408
|
...input.requestedInvocationChannel ? { requestedInvocationChannel: input.requestedInvocationChannel } : {},
|
|
5753
6409
|
...input.effectiveTool ? { effectiveTool: input.effectiveTool } : {},
|
|
5754
6410
|
...input.effectiveInvocationChannel ? { effectiveInvocationChannel: input.effectiveInvocationChannel } : {},
|
|
5755
|
-
...input
|
|
6411
|
+
...normalizeModelConfig(input)
|
|
5756
6412
|
};
|
|
5757
6413
|
}
|
|
6414
|
+
function resolveProviderModelConfig(capability, modelConfig) {
|
|
6415
|
+
const normalized = normalizeModelConfig(modelConfig);
|
|
6416
|
+
if (!hasModelConfig(normalized)) {
|
|
6417
|
+
return { ready: true, config: {} };
|
|
6418
|
+
}
|
|
6419
|
+
if (!capability.supportsModelSelection) {
|
|
6420
|
+
return { ready: false, status: "modelUnsupported", message: `${capability.name} does not support Amistio model selection yet.` };
|
|
6421
|
+
}
|
|
6422
|
+
if (!normalized.providerId && !normalized.modelId && !normalized.modelVariant && (!normalized.reasoningEffort || normalized.reasoningEffort === "auto")) {
|
|
6423
|
+
return { ready: true, config: normalized };
|
|
6424
|
+
}
|
|
6425
|
+
const catalog = capability.providerCatalog;
|
|
6426
|
+
if (!catalog) {
|
|
6427
|
+
return { ready: false, status: "modelUnsupported", message: `${capability.name} does not report a provider model catalog yet.` };
|
|
6428
|
+
}
|
|
6429
|
+
const providerId = normalized.providerId ?? inferProviderIdForModel(catalog, normalized.modelId ?? normalized.model);
|
|
6430
|
+
if (!providerId) {
|
|
6431
|
+
return { ready: false, status: "modelUnsupported", message: "Select a provider id with the provider-backed model preference." };
|
|
6432
|
+
}
|
|
6433
|
+
const provider = catalog[providerId];
|
|
6434
|
+
if (!provider) {
|
|
6435
|
+
return { ready: false, status: "modelUnsupported", message: `${providerId} is not available in ${capability.name}'s provider catalog.` };
|
|
6436
|
+
}
|
|
6437
|
+
const modelId = normalized.modelId ?? inferModelId(provider.models, normalized.model);
|
|
6438
|
+
if (!modelId) {
|
|
6439
|
+
return { ready: false, status: "modelUnsupported", message: "Select a model id with the provider-backed model preference." };
|
|
6440
|
+
}
|
|
6441
|
+
const model = provider.models[modelId];
|
|
6442
|
+
if (!model) {
|
|
6443
|
+
return { ready: false, status: "modelUnsupported", message: `${providerId}/${modelId} is not available in ${capability.name}'s provider catalog.` };
|
|
6444
|
+
}
|
|
6445
|
+
if (normalized.modelVariant) {
|
|
6446
|
+
const variant = model.variants?.[normalized.modelVariant];
|
|
6447
|
+
if (!variant || variant.disabled) {
|
|
6448
|
+
return { ready: false, status: "variantUnsupported", message: `${providerId}/${modelId} does not support variant ${normalized.modelVariant}.` };
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
if (normalized.reasoningEffort && normalized.reasoningEffort !== "auto") {
|
|
6452
|
+
const supportedEfforts = model.supportedReasoningEfforts ?? (model.reasoning ? ["auto", "low", "medium", "high", "xhigh"] : []);
|
|
6453
|
+
if (!supportedEfforts.includes(normalized.reasoningEffort)) {
|
|
6454
|
+
return { ready: false, status: "reasoningUnsupported", message: `${providerId}/${modelId} does not support ${normalized.reasoningEffort} reasoning effort.` };
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
return {
|
|
6458
|
+
ready: true,
|
|
6459
|
+
config: {
|
|
6460
|
+
model: normalized.model ?? `${providerId}/${modelId}`,
|
|
6461
|
+
providerId,
|
|
6462
|
+
modelId,
|
|
6463
|
+
...normalized.modelVariant ? { modelVariant: normalized.modelVariant } : {},
|
|
6464
|
+
...normalized.reasoningEffort ? { reasoningEffort: normalized.reasoningEffort } : {}
|
|
6465
|
+
}
|
|
6466
|
+
};
|
|
6467
|
+
}
|
|
6468
|
+
function normalizeModelConfig(config) {
|
|
6469
|
+
const model = cleanPreferenceText(config.model);
|
|
6470
|
+
const providerId = cleanPreferenceText(config.providerId);
|
|
6471
|
+
const modelId = cleanPreferenceText(config.modelId);
|
|
6472
|
+
const modelVariant = cleanPreferenceText(config.modelVariant);
|
|
6473
|
+
return {
|
|
6474
|
+
...model ? { model } : {},
|
|
6475
|
+
...providerId ? { providerId } : {},
|
|
6476
|
+
...modelId ? { modelId } : {},
|
|
6477
|
+
...modelVariant ? { modelVariant } : {},
|
|
6478
|
+
...config.reasoningEffort ? { reasoningEffort: config.reasoningEffort } : {}
|
|
6479
|
+
};
|
|
6480
|
+
}
|
|
6481
|
+
function hasExplicitModelConfig(config) {
|
|
6482
|
+
return hasModelConfig(normalizeModelConfig(config));
|
|
6483
|
+
}
|
|
6484
|
+
function hasModelConfig(config) {
|
|
6485
|
+
return Boolean(config.model || config.providerId || config.modelId || config.modelVariant || config.reasoningEffort);
|
|
6486
|
+
}
|
|
6487
|
+
function cleanPreferenceText(value) {
|
|
6488
|
+
const trimmed = value?.trim();
|
|
6489
|
+
return trimmed ? trimmed : void 0;
|
|
6490
|
+
}
|
|
6491
|
+
function inferProviderIdForModel(catalog, model) {
|
|
6492
|
+
if (!model) return void 0;
|
|
6493
|
+
if (model.includes("/")) {
|
|
6494
|
+
const [providerId, modelId] = model.split("/", 2);
|
|
6495
|
+
if (providerId && modelId && catalog[providerId]?.models[modelId]) return providerId;
|
|
6496
|
+
}
|
|
6497
|
+
return Object.entries(catalog).find(([, provider]) => Boolean(provider.models[model]))?.[0];
|
|
6498
|
+
}
|
|
6499
|
+
function inferModelId(models, model) {
|
|
6500
|
+
if (!model) return void 0;
|
|
6501
|
+
if (models[model]) return model;
|
|
6502
|
+
const slashIndex = model.indexOf("/");
|
|
6503
|
+
if (slashIndex !== -1) {
|
|
6504
|
+
const modelId = model.slice(slashIndex + 1);
|
|
6505
|
+
if (models[modelId]) return modelId;
|
|
6506
|
+
}
|
|
6507
|
+
return void 0;
|
|
6508
|
+
}
|
|
6509
|
+
function toolConfigModelOptions(toolConfig) {
|
|
6510
|
+
return normalizeModelConfig(toolConfig);
|
|
6511
|
+
}
|
|
6512
|
+
function localModelConfigOptions(options) {
|
|
6513
|
+
return normalizeModelConfig({
|
|
6514
|
+
model: options.model,
|
|
6515
|
+
providerId: options.providerId ?? options.provider,
|
|
6516
|
+
modelId: options.modelId,
|
|
6517
|
+
modelVariant: options.modelVariant,
|
|
6518
|
+
reasoningEffort: options.reasoningEffort
|
|
6519
|
+
});
|
|
6520
|
+
}
|
|
5758
6521
|
function capabilitySupportsInvocationChannel(capability, channel) {
|
|
5759
6522
|
if (channel === "auto") return capability.available;
|
|
5760
6523
|
if (channel === "sdk") return capability.sdkAvailable;
|
|
@@ -5787,6 +6550,8 @@ function runnerIsolationCapabilityMetadata() {
|
|
|
5787
6550
|
};
|
|
5788
6551
|
}
|
|
5789
6552
|
function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
|
|
6553
|
+
const modelConfig = toolConfig ? toolConfigModelOptions(toolConfig) : {};
|
|
6554
|
+
const effectiveModelConfig = toolConfig?.ready ? modelConfig : {};
|
|
5790
6555
|
return {
|
|
5791
6556
|
version: CLI_VERSION,
|
|
5792
6557
|
mode,
|
|
@@ -5796,16 +6561,24 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
|
|
|
5796
6561
|
...toolConfig?.capabilities ? { capabilities: toolConfig.capabilities } : {},
|
|
5797
6562
|
...toolConfig?.requestedTool ? { requestedTool: toolConfig.requestedTool } : {},
|
|
5798
6563
|
...toolConfig?.requestedInvocationChannel ? { requestedInvocationChannel: toolConfig.requestedInvocationChannel } : {},
|
|
6564
|
+
...modelConfig.providerId ? { requestedProviderId: modelConfig.providerId } : {},
|
|
6565
|
+
...modelConfig.modelId ? { requestedModelId: modelConfig.modelId } : {},
|
|
6566
|
+
...modelConfig.modelVariant ? { requestedModelVariant: modelConfig.modelVariant } : {},
|
|
6567
|
+
...modelConfig.reasoningEffort ? { requestedReasoningEffort: modelConfig.reasoningEffort } : {},
|
|
5799
6568
|
...toolConfig?.effectiveTool ? { effectiveTool: toolConfig.effectiveTool } : {},
|
|
5800
6569
|
...toolConfig?.effectiveInvocationChannel ? { effectiveInvocationChannel: toolConfig.effectiveInvocationChannel } : {},
|
|
5801
|
-
...
|
|
6570
|
+
...effectiveModelConfig.model ? { effectiveModel: effectiveModelConfig.model } : {},
|
|
6571
|
+
...effectiveModelConfig.providerId ? { effectiveProviderId: effectiveModelConfig.providerId } : {},
|
|
6572
|
+
...effectiveModelConfig.modelId ? { effectiveModelId: effectiveModelConfig.modelId } : {},
|
|
6573
|
+
...effectiveModelConfig.modelVariant ? { effectiveModelVariant: effectiveModelConfig.modelVariant } : {},
|
|
6574
|
+
...effectiveModelConfig.reasoningEffort ? { effectiveReasoningEffort: effectiveModelConfig.reasoningEffort } : {},
|
|
5802
6575
|
...toolConfig?.source ? { preferenceSource: toolConfig.source } : {},
|
|
5803
6576
|
...toolConfig?.status ? { preferenceStatus: toolConfig.status } : {},
|
|
5804
6577
|
...toolConfig?.message ? { preferenceMessage: toolConfig.message } : {}
|
|
5805
6578
|
};
|
|
5806
6579
|
}
|
|
5807
6580
|
function runnerMachineId() {
|
|
5808
|
-
return
|
|
6581
|
+
return createHash7("sha256").update(`${os7.hostname()}:${os7.platform()}:${os7.arch()}`).digest("hex").slice(0, 20);
|
|
5809
6582
|
}
|
|
5810
6583
|
async function delay(milliseconds) {
|
|
5811
6584
|
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|