aui-agent-builder 0.3.101 → 0.3.103
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 +11 -17
- package/dist/api-client/kb-view-client.d.ts +26 -12
- package/dist/api-client/kb-view-client.d.ts.map +1 -1
- package/dist/api-client/kb-view-client.js.map +1 -1
- package/dist/commands/add-integration.d.ts.map +1 -1
- package/dist/commands/add-integration.js +18 -47
- package/dist/commands/add-integration.js.map +1 -1
- package/dist/commands/add-tool.d.ts.map +1 -1
- package/dist/commands/add-tool.js +15 -69
- package/dist/commands/add-tool.js.map +1 -1
- package/dist/commands/import-agent.d.ts +1 -0
- package/dist/commands/import-agent.d.ts.map +1 -1
- package/dist/commands/import-agent.js +24 -87
- package/dist/commands/import-agent.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +43 -60
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pull-agent.d.ts +1 -0
- package/dist/commands/pull-agent.d.ts.map +1 -1
- package/dist/commands/pull-agent.js +23 -80
- package/dist/commands/pull-agent.js.map +1 -1
- package/dist/commands/push.d.ts +45 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +171 -204
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/rag.js +1 -1
- package/dist/commands/rag.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +33 -121
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/widget.d.ts.map +1 -1
- package/dist/commands/widget.js +21 -140
- package/dist/commands/widget.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/diff.service.d.ts.map +1 -1
- package/dist/services/diff.service.js +3 -37
- package/dist/services/diff.service.js.map +1 -1
- package/dist/services/kb-view.service.d.ts +12 -27
- package/dist/services/kb-view.service.d.ts.map +1 -1
- package/dist/services/kb-view.service.js +134 -106
- package/dist/services/kb-view.service.js.map +1 -1
- package/dist/services/pull-schema.service.d.ts.map +1 -1
- package/dist/services/pull-schema.service.js +3 -5
- package/dist/services/pull-schema.service.js.map +1 -1
- package/dist/telemetry.d.ts +19 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +24 -2
- package/dist/telemetry.js.map +1 -1
- package/dist/utils/index.d.ts +2 -70
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +35 -243
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/push.js
CHANGED
|
@@ -7,7 +7,7 @@ import inquirer from "inquirer";
|
|
|
7
7
|
import { getConfig, loadProjectConfig, saveProjectConfig, loadAgentSettingsApiKey, saveAgentSettingsApiKey, } from "../config/index.js";
|
|
8
8
|
import { getValidSession } from "../services/auth.service.js";
|
|
9
9
|
import { AUIClient, applyScopeLevel } from "../api-client/index.js";
|
|
10
|
-
import { findAuiFiles, parseAuiFile
|
|
10
|
+
import { findAuiFiles, parseAuiFile } from "../utils/index.js";
|
|
11
11
|
import { validate } from "./validate.js";
|
|
12
12
|
import { getTracer, SpanStatusCode, setUserContext, setAgentContext, } from "../telemetry.js";
|
|
13
13
|
import { trace } from "@opentelemetry/api";
|
|
@@ -295,84 +295,35 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
295
295
|
const fileData = [];
|
|
296
296
|
try {
|
|
297
297
|
const files = await findAuiFiles(projectRoot);
|
|
298
|
-
// First pass: index sibling widget files by their tool slug so we can
|
|
299
|
-
// fold them back into the parent tool when we encounter the tool file.
|
|
300
|
-
// This keeps the in-memory push payload identical to the legacy layout
|
|
301
|
-
// where `widget` was inline on the tool.
|
|
302
|
-
const widgetBySlug = new Map();
|
|
303
|
-
for (const file of files) {
|
|
304
|
-
const rel = path.relative(projectRoot, file).replace(/\\/g, "/");
|
|
305
|
-
const cls = classifyAuiPath(rel);
|
|
306
|
-
if (cls.kind === "tool-widget" && cls.slug) {
|
|
307
|
-
widgetBySlug.set(cls.slug, file);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
298
|
for (const file of files) {
|
|
311
299
|
const parsed = parseAuiFile(file);
|
|
312
300
|
if (!parsed)
|
|
313
301
|
continue;
|
|
314
302
|
const relativePath = path.relative(projectRoot, file);
|
|
315
|
-
const normalized = relativePath.replace(/\\/g, "/");
|
|
316
|
-
const cls = classifyAuiPath(normalized);
|
|
317
303
|
let type = "unknown";
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// object so downstream task-builders see a "tool with widget"
|
|
342
|
-
// shape regardless of how the project is laid out on disk.
|
|
343
|
-
if (cls.slug && widgetBySlug.has(cls.slug)) {
|
|
344
|
-
const widget = parseAuiFile(widgetBySlug.get(cls.slug));
|
|
345
|
-
const toolObj = parsed.tool ??
|
|
346
|
-
parsed;
|
|
347
|
-
const hasWidget = widget && Object.keys(widget).length > 0 ? widget : null;
|
|
348
|
-
const mergedTool = { ...toolObj, widget: hasWidget };
|
|
349
|
-
data = { tool: mergedTool };
|
|
350
|
-
}
|
|
351
|
-
break;
|
|
352
|
-
case "tool-widget":
|
|
353
|
-
// Folded into the parent tool above — don't emit a standalone
|
|
354
|
-
// entry. The push machinery treats widget edits as edits to the
|
|
355
|
-
// tool (the diff machinery handles file-level change detection).
|
|
356
|
-
continue;
|
|
357
|
-
default: {
|
|
358
|
-
// Fall back to the historical "guess by top-level key" behaviour
|
|
359
|
-
// so any unusual files keep working.
|
|
360
|
-
const isToolPath = normalized.startsWith("tools/") || normalized.startsWith("tools\\");
|
|
361
|
-
if (parsed.general_settings)
|
|
362
|
-
type = "general_settings";
|
|
363
|
-
else if (parsed.tool || isToolPath)
|
|
364
|
-
type = "tool";
|
|
365
|
-
else if (parsed.parameters)
|
|
366
|
-
type = "parameters";
|
|
367
|
-
else if (parsed.entities)
|
|
368
|
-
type = "entities";
|
|
369
|
-
else if (parsed.integrations)
|
|
370
|
-
type = "integrations";
|
|
371
|
-
else if (parsed.rules)
|
|
372
|
-
type = "rules";
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
fileData.push({ file: relativePath, type, data });
|
|
304
|
+
const isToolPath = relativePath.startsWith("tools/") || relativePath.startsWith("tools\\");
|
|
305
|
+
// Recognize agent.aui.json by filename (not just by the wrapper key),
|
|
306
|
+
// mirroring `validate.tsx:222` which accepts both
|
|
307
|
+
// { "general_settings": {...} } (wrapped — `aui pull` writes this)
|
|
308
|
+
// {...top-level fields...} (flat — Agent Builder skills write this)
|
|
309
|
+
// Without this, a flat agent.aui.json fell through to type "unknown"
|
|
310
|
+
// and `buildPushTasks` never had a chance to emit a
|
|
311
|
+
// patch-general-settings task — same "silently dropped" class of bug
|
|
312
|
+
// the tool-modify branch had.
|
|
313
|
+
const isSettingsPath = relativePath === "agent.aui.json";
|
|
314
|
+
if (parsed.general_settings || isSettingsPath)
|
|
315
|
+
type = "general_settings";
|
|
316
|
+
else if (parsed.tool || isToolPath)
|
|
317
|
+
type = "tool";
|
|
318
|
+
else if (parsed.parameters)
|
|
319
|
+
type = "parameters";
|
|
320
|
+
else if (parsed.entities)
|
|
321
|
+
type = "entities";
|
|
322
|
+
else if (parsed.integrations)
|
|
323
|
+
type = "integrations";
|
|
324
|
+
else if (parsed.rules)
|
|
325
|
+
type = "rules";
|
|
326
|
+
fileData.push({ file: relativePath, type, data: parsed });
|
|
376
327
|
}
|
|
377
328
|
if (json)
|
|
378
329
|
stderrLog(`Read ${fileData.length} files`);
|
|
@@ -611,12 +562,18 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
611
562
|
pushSpan.setAttribute("push.diff.modified", diff.totalModified);
|
|
612
563
|
pushSpan.setAttribute("push.diff.deleted", diff.totalDeleted);
|
|
613
564
|
}
|
|
614
|
-
|
|
565
|
+
// pushKnowledgeHubs runs as a separate step; short-circuiting on
|
|
566
|
+
// pushTasks alone would silently no-op KB-only pushes.
|
|
567
|
+
const hasKbChanges = diff?.changedFiles?.some((c) => c.file.startsWith("knowledge-hubs/")) ??
|
|
568
|
+
false;
|
|
569
|
+
if (pushTasks.length === 0 && !hasKbChanges) {
|
|
615
570
|
pushSpan.setAttribute("push.exit_reason", "no_pushable_tasks");
|
|
616
571
|
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "success", label: "No pushable changes detected." }) }));
|
|
617
572
|
return;
|
|
618
573
|
}
|
|
619
|
-
|
|
574
|
+
if (pushTasks.length > 0) {
|
|
575
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "info", label: `Pushing ${pushTasks.length} change(s)...` }) }));
|
|
576
|
+
}
|
|
620
577
|
let succeeded = 0;
|
|
621
578
|
let failed = 0;
|
|
622
579
|
let authFailed = false;
|
|
@@ -1592,7 +1549,7 @@ async function pushKnowledgeHubs(projectRoot, projectConfig) {
|
|
|
1592
1549
|
scope,
|
|
1593
1550
|
created_by: userId,
|
|
1594
1551
|
knowledge_base_name: kbData.name,
|
|
1595
|
-
knowledge_base_description: kbData.description,
|
|
1552
|
+
knowledge_base_description: kbData.description ?? undefined,
|
|
1596
1553
|
});
|
|
1597
1554
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
1598
1555
|
if (importResult.knowledge_base_id) {
|
|
@@ -1635,6 +1592,69 @@ async function pushKnowledgeHubs(projectRoot, projectConfig) {
|
|
|
1635
1592
|
}
|
|
1636
1593
|
});
|
|
1637
1594
|
}
|
|
1595
|
+
// importFiles ingests binaries only; sidecar edits + non-file
|
|
1596
|
+
// Resources reach the server through importJson. After importFiles
|
|
1597
|
+
// so binary re-extracts don't clobber manifest edits.
|
|
1598
|
+
const hasJsonPayload = kbData.tabular_files.length > 0 || kbData.markdown_files.length > 0;
|
|
1599
|
+
if (hasJsonPayload) {
|
|
1600
|
+
const kbJsonTracer = getTracer();
|
|
1601
|
+
await kbJsonTracer.startActiveSpan("aui.push.task.kb-import-json", async (span) => {
|
|
1602
|
+
span.setAttribute("push.task.type", "kb-import-json");
|
|
1603
|
+
span.setAttribute("push.task.label", `Sync knowledge base manifest: ${kbData.name || kbDirName}`);
|
|
1604
|
+
span.setAttribute("push.task.file", `knowledge-hubs/${kbDirName}/kb.json`);
|
|
1605
|
+
span.setAttribute("push.task.kb_name", kbData.name || kbDirName);
|
|
1606
|
+
span.setAttribute("push.task.tabular_count", kbData.tabular_files.length);
|
|
1607
|
+
span.setAttribute("push.task.markdown_count", kbData.markdown_files.length);
|
|
1608
|
+
await setUserContext(span);
|
|
1609
|
+
await setAgentContext(span, {
|
|
1610
|
+
agentId: projectConfig.agent_management_id,
|
|
1611
|
+
agentCode: projectConfig.agent_code,
|
|
1612
|
+
versionId: projectConfig.version_id,
|
|
1613
|
+
versionLabel: projectConfig.version_label,
|
|
1614
|
+
networkId: kbNetworkId,
|
|
1615
|
+
accountId: projectConfig.account_id,
|
|
1616
|
+
organizationId: projectConfig.organization_id,
|
|
1617
|
+
networkCategoryId: projectConfig.network_category_id,
|
|
1618
|
+
});
|
|
1619
|
+
try {
|
|
1620
|
+
await kbViewClient.importJson({
|
|
1621
|
+
scope,
|
|
1622
|
+
knowledge_bases: [
|
|
1623
|
+
{
|
|
1624
|
+
name: kbData.name,
|
|
1625
|
+
description: kbData.description,
|
|
1626
|
+
tabular_files: kbData.tabular_files,
|
|
1627
|
+
markdown_files: kbData.markdown_files,
|
|
1628
|
+
},
|
|
1629
|
+
],
|
|
1630
|
+
created_by: userId,
|
|
1631
|
+
network_id: scope.network_id,
|
|
1632
|
+
});
|
|
1633
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1634
|
+
}
|
|
1635
|
+
catch (importErr) {
|
|
1636
|
+
hadUploadFailure = true;
|
|
1637
|
+
const errMsg = importErr instanceof Error
|
|
1638
|
+
? importErr.message
|
|
1639
|
+
: String(importErr);
|
|
1640
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: errMsg });
|
|
1641
|
+
span.recordException(importErr instanceof Error ? importErr : new Error(errMsg));
|
|
1642
|
+
span.setAttribute("push.task.error", errMsg);
|
|
1643
|
+
if (importErr.statusCode) {
|
|
1644
|
+
span.setAttribute("push.task.error_status_code", importErr.statusCode);
|
|
1645
|
+
}
|
|
1646
|
+
failures.push({
|
|
1647
|
+
label: `Sync knowledge base manifest: ${kbData.name || kbDirName}`,
|
|
1648
|
+
file: `knowledge-hubs/${kbDirName}/kb.json`,
|
|
1649
|
+
error: errMsg,
|
|
1650
|
+
});
|
|
1651
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "error", label: `Failed to sync manifest for "${kbData.name || kbDirName}": ${errMsg}` }) }));
|
|
1652
|
+
}
|
|
1653
|
+
finally {
|
|
1654
|
+
span.end();
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1638
1658
|
}
|
|
1639
1659
|
if (hadUploadFailure) {
|
|
1640
1660
|
kbSpinner.fail(`Knowledge base push completed with errors`);
|
|
@@ -1689,10 +1709,7 @@ function getArrayFileInfoForPush(filePath, dir) {
|
|
|
1689
1709
|
return null;
|
|
1690
1710
|
const content = JSON.parse(fs.readFileSync(fullPath, "utf-8"));
|
|
1691
1711
|
const isToolPath = filePath.startsWith("tools/") || filePath.startsWith("tools\\");
|
|
1692
|
-
|
|
1693
|
-
// array-of-entities files — they don't need item-level diffing.
|
|
1694
|
-
const isPerIntegration = filePath.startsWith("integrations/") || filePath.startsWith("integrations\\");
|
|
1695
|
-
if (content.tool || isToolPath || content.integration || isPerIntegration)
|
|
1712
|
+
if (content.tool || isToolPath)
|
|
1696
1713
|
return null;
|
|
1697
1714
|
if (content.parameters)
|
|
1698
1715
|
return { arrayKey: "parameters", itemKey: "code", label: "Parameters" };
|
|
@@ -1852,59 +1869,28 @@ function diffObjects(oldObj, newObj, prefix = "") {
|
|
|
1852
1869
|
}
|
|
1853
1870
|
return diffs;
|
|
1854
1871
|
}
|
|
1855
|
-
|
|
1872
|
+
/**
|
|
1873
|
+
* Build the ordered list of API tasks the push will execute, given a git
|
|
1874
|
+
* diff summary, the parsed file contents, and an injectable per-file diff
|
|
1875
|
+
* function.
|
|
1876
|
+
*
|
|
1877
|
+
* `getFileDiffFn` is parameterized so tests can stub out `git show` without
|
|
1878
|
+
* a real git repo. `getItemLevelDiffFn` is optional and defaults to the
|
|
1879
|
+
* production implementation; tests inject it so the array-keyed branches
|
|
1880
|
+
* (parameters, entities, integrations) are exercisable without git too.
|
|
1881
|
+
*
|
|
1882
|
+
* Exported for unit-test access only — production callers should keep
|
|
1883
|
+
* going through `_push`, which threads in the real `getFileDiff` and
|
|
1884
|
+
* `getItemLevelDiff` from `../utils/git.js`.
|
|
1885
|
+
*/
|
|
1886
|
+
export function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn, getItemLevelDiffFn) {
|
|
1887
|
+
const getItems = getItemLevelDiffFn ?? getItemLevelDiff;
|
|
1856
1888
|
const tasks = [];
|
|
1857
|
-
// Up-cast: a change to the sibling widget file is semantically a change
|
|
1858
|
-
// to its parent tool, not a standalone entity. Rewrite each such row so
|
|
1859
|
-
// the downstream task-builder picks up the merged tool from `fileData`.
|
|
1860
|
-
//
|
|
1861
|
-
// We resolve the *actual* on-disk tool file for the slug by consulting
|
|
1862
|
-
// `fileData` (which holds the canonical relative paths emitted by the
|
|
1863
|
-
// file walker), so the up-cast is filename-naming-scheme agnostic — it
|
|
1864
|
-
// works for `<slug>_tool.aui.json`, the prior `tool.aui.json`, and the
|
|
1865
|
-
// pre-split legacy `tools/<slug>.aui.json`.
|
|
1866
|
-
const toolFileBySlug = new Map();
|
|
1867
|
-
for (const fd of fileData) {
|
|
1868
|
-
if (fd.type !== "tool")
|
|
1869
|
-
continue;
|
|
1870
|
-
const normalized = fd.file.replace(/\\/g, "/");
|
|
1871
|
-
const cls = classifyAuiPath(normalized);
|
|
1872
|
-
if (cls.kind === "tool" && cls.slug) {
|
|
1873
|
-
toolFileBySlug.set(cls.slug, normalized);
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
const consolidatedFiles = [];
|
|
1877
|
-
const seenToolFiles = new Set();
|
|
1878
1889
|
for (const change of diff.changedFiles) {
|
|
1879
|
-
const normalized = change.file.replace(/\\/g, "/");
|
|
1880
|
-
const cls = classifyAuiPath(normalized);
|
|
1881
|
-
if (cls.kind === "tool-widget" && cls.slug) {
|
|
1882
|
-
const toolFile = toolFileBySlug.get(cls.slug);
|
|
1883
|
-
// If the tool file isn't on disk anymore (mid-edit, half-written
|
|
1884
|
-
// project), there's nothing for the widget change to attach to.
|
|
1885
|
-
// Drop the row — push will simply not patch this tool.
|
|
1886
|
-
if (!toolFile)
|
|
1887
|
-
continue;
|
|
1888
|
-
if (seenToolFiles.has(toolFile))
|
|
1889
|
-
continue;
|
|
1890
|
-
seenToolFiles.add(toolFile);
|
|
1891
|
-
consolidatedFiles.push({ status: "modified", file: toolFile });
|
|
1892
|
-
continue;
|
|
1893
|
-
}
|
|
1894
|
-
if (cls.kind === "tool" && cls.slug) {
|
|
1895
|
-
seenToolFiles.add(change.file);
|
|
1896
|
-
}
|
|
1897
|
-
consolidatedFiles.push(change);
|
|
1898
|
-
}
|
|
1899
|
-
for (const change of consolidatedFiles) {
|
|
1900
|
-
const normalizedChange = change.file.replace(/\\/g, "/");
|
|
1901
|
-
const changeCls = classifyAuiPath(normalizedChange);
|
|
1902
1890
|
if (change.status === "deleted") {
|
|
1903
|
-
const isToolFile =
|
|
1904
|
-
(change.file.includes("tools/") && change.file.endsWith(".aui.json"));
|
|
1891
|
+
const isToolFile = change.file.includes("tools/") && change.file.endsWith(".aui.json");
|
|
1905
1892
|
if (isToolFile) {
|
|
1906
|
-
const basename = (
|
|
1907
|
-
change.file.split("/").pop().replace(".aui.json", ""));
|
|
1893
|
+
const basename = change.file.split("/").pop().replace(".aui.json", "");
|
|
1908
1894
|
const toolName = basename.toUpperCase().replace(/-/g, "_");
|
|
1909
1895
|
tasks.push({
|
|
1910
1896
|
type: "delete-tool",
|
|
@@ -1913,17 +1899,6 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
1913
1899
|
file: change.file,
|
|
1914
1900
|
body: {},
|
|
1915
1901
|
});
|
|
1916
|
-
continue;
|
|
1917
|
-
}
|
|
1918
|
-
if (changeCls.kind === "integration") {
|
|
1919
|
-
const code = changeCls.slug;
|
|
1920
|
-
tasks.push({
|
|
1921
|
-
type: "delete-integration",
|
|
1922
|
-
label: `Delete integration: ${code}`,
|
|
1923
|
-
file: change.file,
|
|
1924
|
-
body: {},
|
|
1925
|
-
itemCode: code,
|
|
1926
|
-
});
|
|
1927
1902
|
}
|
|
1928
1903
|
continue;
|
|
1929
1904
|
}
|
|
@@ -1932,33 +1907,6 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
1932
1907
|
f.file.endsWith("/" + change.file));
|
|
1933
1908
|
if (!fd)
|
|
1934
1909
|
continue;
|
|
1935
|
-
// ── Per-file integration change (new layout) ──
|
|
1936
|
-
if (fd.type === "integration") {
|
|
1937
|
-
const integrationObj = (fd.data.integration ??
|
|
1938
|
-
fd.data);
|
|
1939
|
-
const code = integrationObj.code ?? changeCls.slug ?? "";
|
|
1940
|
-
if (!code)
|
|
1941
|
-
continue;
|
|
1942
|
-
if (change.status === "added") {
|
|
1943
|
-
tasks.push({
|
|
1944
|
-
type: "create-integration",
|
|
1945
|
-
label: `Create integration: ${code}`,
|
|
1946
|
-
file: change.file,
|
|
1947
|
-
body: integrationObj,
|
|
1948
|
-
itemCode: code,
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
else if (change.status === "modified") {
|
|
1952
|
-
tasks.push({
|
|
1953
|
-
type: "patch-integration",
|
|
1954
|
-
label: `Update integration: ${code}`,
|
|
1955
|
-
file: change.file,
|
|
1956
|
-
body: integrationObj,
|
|
1957
|
-
itemCode: code,
|
|
1958
|
-
});
|
|
1959
|
-
}
|
|
1960
|
-
continue;
|
|
1961
|
-
}
|
|
1962
1910
|
if (fd.type === "tool") {
|
|
1963
1911
|
const toolData = fd.data?.tool || fd.data;
|
|
1964
1912
|
if (!toolData || !toolData.code)
|
|
@@ -1974,22 +1922,29 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
1974
1922
|
});
|
|
1975
1923
|
}
|
|
1976
1924
|
else if (change.status === "modified") {
|
|
1977
|
-
//
|
|
1978
|
-
//
|
|
1979
|
-
//
|
|
1980
|
-
//
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1925
|
+
// `change.status === "modified"` comes from `getDiffSummary` (git
|
|
1926
|
+
// status). That alone is the source of truth that the file content
|
|
1927
|
+
// diverged from the baseline; always push the patch. The full
|
|
1928
|
+
// `toolData` body is sent (consistent with the create-tool branch
|
|
1929
|
+
// immediately above); the platform PATCH is idempotent for fields
|
|
1930
|
+
// whose value didn't actually change, so this is safe.
|
|
1931
|
+
//
|
|
1932
|
+
// Pre-fix this branch routed the changes through a
|
|
1933
|
+
// `buildToolPatchBody` gate that only matched diff paths starting
|
|
1934
|
+
// with the wrapped-shape `tool.` prefix. Tool files written in flat
|
|
1935
|
+
// shape (no `{ "tool": {...} }` wrapper — accepted by the loader at
|
|
1936
|
+
// ~L2675 and by `aui validate`) diffed to top-level paths
|
|
1937
|
+
// (`response_type`, `widget`, `goal`, …), the gate returned an
|
|
1938
|
+
// empty body, and the `patch-tool` task was silently dropped.
|
|
1939
|
+
// Symptom users hit: `aui diff` showed the change, `aui push`
|
|
1940
|
+
// printed "No pushable changes detected" and exited 0.
|
|
1941
|
+
tasks.push({
|
|
1942
|
+
type: "patch-tool",
|
|
1943
|
+
label: `Update tool: ${toolCode}`,
|
|
1944
|
+
toolName,
|
|
1945
|
+
file: change.file,
|
|
1946
|
+
body: toolData,
|
|
1947
|
+
});
|
|
1993
1948
|
}
|
|
1994
1949
|
}
|
|
1995
1950
|
else if (fd.type === "general_settings") {
|
|
@@ -2006,7 +1961,14 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
2006
1961
|
}
|
|
2007
1962
|
}
|
|
2008
1963
|
else if (change.status === "added") {
|
|
2009
|
-
|
|
1964
|
+
// Mirror the tool loader's `(fd.data as any)?.tool || fd.data`
|
|
1965
|
+
// tolerance: accept both `{ "general_settings": {...} }` (wrapped)
|
|
1966
|
+
// and `{...}` (flat). `normalizeGeneralSettings` in
|
|
1967
|
+
// `pull-agent.tsx` / `import-agent.tsx` and `aui validate`
|
|
1968
|
+
// (`commands/validate.tsx:222`) already permit both layouts —
|
|
1969
|
+
// staying in lockstep with them here prevents the same
|
|
1970
|
+
// "silently dropped" class of bug the tool-modify branch had.
|
|
1971
|
+
const settings = fd.data?.general_settings ?? fd.data;
|
|
2010
1972
|
if (settings) {
|
|
2011
1973
|
tasks.push({
|
|
2012
1974
|
type: "patch-general-settings",
|
|
@@ -2018,7 +1980,7 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
2018
1980
|
}
|
|
2019
1981
|
}
|
|
2020
1982
|
else if (fd.type === "parameters") {
|
|
2021
|
-
const itemDiffs =
|
|
1983
|
+
const itemDiffs = getItems(projectRoot, change.file, "parameters", "code");
|
|
2022
1984
|
for (const item of itemDiffs) {
|
|
2023
1985
|
if (item.status === "added") {
|
|
2024
1986
|
tasks.push({
|
|
@@ -2050,7 +2012,7 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
2050
2012
|
}
|
|
2051
2013
|
}
|
|
2052
2014
|
else if (fd.type === "entities") {
|
|
2053
|
-
const itemDiffs =
|
|
2015
|
+
const itemDiffs = getItems(projectRoot, change.file, "entities", "name");
|
|
2054
2016
|
for (const item of itemDiffs) {
|
|
2055
2017
|
if (item.status === "added") {
|
|
2056
2018
|
tasks.push({
|
|
@@ -2082,7 +2044,7 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
2082
2044
|
}
|
|
2083
2045
|
}
|
|
2084
2046
|
else if (fd.type === "integrations") {
|
|
2085
|
-
const itemDiffs =
|
|
2047
|
+
const itemDiffs = getItems(projectRoot, change.file, "integrations", "code");
|
|
2086
2048
|
for (const item of itemDiffs) {
|
|
2087
2049
|
if (item.status === "added") {
|
|
2088
2050
|
tasks.push({
|
|
@@ -2125,29 +2087,34 @@ function buildPushTasks(diff, fileData, projectRoot, getFileDiffFn) {
|
|
|
2125
2087
|
}
|
|
2126
2088
|
return tasks;
|
|
2127
2089
|
}
|
|
2128
|
-
|
|
2090
|
+
// NOTE: `buildToolPatchBody` was removed when the tool-modify branch above
|
|
2091
|
+
// switched to pushing on any `change.status === "modified"`. Its sole job
|
|
2092
|
+
// had been to gate the patch-tool task on a `tool.<field>` diff-path prefix,
|
|
2093
|
+
// which silently dropped every edit on flat-shape (no-wrapper) tool files.
|
|
2094
|
+
// See the inline comment in the modify branch (~L2688) for the full history.
|
|
2095
|
+
export function buildSettingsPatchBody(fieldDiffs) {
|
|
2129
2096
|
const body = {};
|
|
2130
2097
|
for (const fd of fieldDiffs) {
|
|
2131
2098
|
if (fd.operation === "removed")
|
|
2132
2099
|
continue;
|
|
2133
2100
|
const parts = fd.path.split(".");
|
|
2134
|
-
if (parts
|
|
2135
|
-
const topLevelKey = parts[1];
|
|
2136
|
-
body[topLevelKey] = fd.newValue;
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
return body;
|
|
2140
|
-
}
|
|
2141
|
-
function buildSettingsPatchBody(fieldDiffs) {
|
|
2142
|
-
const body = {};
|
|
2143
|
-
for (const fd of fieldDiffs) {
|
|
2144
|
-
if (fd.operation === "removed")
|
|
2101
|
+
if (parts.length === 0 || !parts[0])
|
|
2145
2102
|
continue;
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
}
|
|
2103
|
+
// general_settings (agent.aui.json) files come in two equally-supported
|
|
2104
|
+
// shapes:
|
|
2105
|
+
// wrapped: { "general_settings": { ...fields... } } → paths look like
|
|
2106
|
+
// `general_settings.<field>...`
|
|
2107
|
+
// flat: { ...fields... } → paths look like
|
|
2108
|
+
// `<field>...`
|
|
2109
|
+
// `aui validate` (`commands/validate.tsx:222`) and
|
|
2110
|
+
// `normalizeGeneralSettings` in `import-agent.tsx` / `pull-agent.tsx`
|
|
2111
|
+
// already accept both layouts. Pre-fix this body builder only matched
|
|
2112
|
+
// the wrapped form, which silently dropped edits to flat-shape files.
|
|
2113
|
+
// Match the loader behavior here so every change reaches the platform.
|
|
2114
|
+
const topLevelKey = parts[0] === "general_settings" && parts.length >= 2
|
|
2115
|
+
? parts[1]
|
|
2116
|
+
: parts[0];
|
|
2117
|
+
body[topLevelKey] = fd.newValue;
|
|
2151
2118
|
}
|
|
2152
2119
|
return body;
|
|
2153
2120
|
}
|