unbrowse 9.0.1 → 9.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unbrowse",
3
- "version": "9.0.1",
3
+ "version": "9.0.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/unbrowse-ai/unbrowse.git"
package/runtime/cli.js CHANGED
@@ -177653,6 +177653,49 @@ function writeSkillSnapshot(cacheKey2, skill) {
177653
177653
  return;
177654
177654
  }
177655
177655
  }
177656
+ function compositeSnapshotDir() {
177657
+ return process.env.UNBROWSE_COMPOSITE_DIR ?? join45(process.env.HOME ?? "/tmp", ".unbrowse", "composites");
177658
+ }
177659
+ function compositeAddress(domain, target, steps, edges) {
177660
+ const canonical2 = [
177661
+ `domain:${domain}`,
177662
+ `target:${target}`,
177663
+ `steps:${steps.map((s) => s.endpoint_id).join(">")}`,
177664
+ `edges:${edges.map((e) => `${e.from}.${e.binding}->${e.to}`).sort().join("|")}`
177665
+ ].join("::");
177666
+ return `composite:${createHash35("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
177667
+ }
177668
+ function compositeLookupKey(domain, target) {
177669
+ const canonical2 = `domain:${domain}::target:${target}`;
177670
+ return `lookup:${createHash35("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
177671
+ }
177672
+ function compositeFilePath(lookupKey2) {
177673
+ return join45(compositeSnapshotDir(), `${lookupKey2.replace(/[^a-z0-9]/gi, "_")}.json`);
177674
+ }
177675
+ function writeComposite(c) {
177676
+ if (process.env.UNBROWSE_STATELESS === "1")
177677
+ return;
177678
+ if (process.env.UNBROWSE_LOCAL_CACHES !== "1")
177679
+ return;
177680
+ try {
177681
+ mkdirSync21(compositeSnapshotDir(), { recursive: true });
177682
+ const target = compositeFilePath(compositeLookupKey(c.domain, c.target));
177683
+ writeFileSync18(target, JSON.stringify(c), "utf-8");
177684
+ return target;
177685
+ } catch {
177686
+ return;
177687
+ }
177688
+ }
177689
+ function readComposite(domain, target) {
177690
+ try {
177691
+ const path17 = compositeFilePath(compositeLookupKey(domain, target));
177692
+ if (!existsSync38(path17))
177693
+ return;
177694
+ return JSON.parse(readFileSync26(path17, "utf-8"));
177695
+ } catch {
177696
+ return;
177697
+ }
177698
+ }
177656
177699
  function hasSearchBindings(endpoint) {
177657
177700
  const haystack = JSON.stringify({
177658
177701
  url: endpoint.url_template,
@@ -178709,6 +178752,27 @@ function extractSearchTermsFromIntent(intent) {
178709
178752
  }
178710
178753
  return terms || null;
178711
178754
  }
178755
+ function buildCompositeEdges(target, steps, boundKeys) {
178756
+ return boundKeys.map((binding) => {
178757
+ const from = steps.find((s) => s.yielded.includes(binding));
178758
+ return from ? { from: from.endpoint_id, binding, to: target } : null;
178759
+ }).filter((e) => e !== null);
178760
+ }
178761
+ function planPrereqOrder(livePrereqOrder, persisted, isReplayable) {
178762
+ if (persisted && persisted.steps.length > 0) {
178763
+ const recordedOrder = persisted.steps.map((s) => s.endpoint_id);
178764
+ if (recordedOrder.every((id) => isReplayable(id))) {
178765
+ return {
178766
+ prereqOrder: [
178767
+ ...recordedOrder,
178768
+ ...livePrereqOrder.filter((p) => !recordedOrder.includes(p))
178769
+ ],
178770
+ replayedCompositeId: persisted.composite_id
178771
+ };
178772
+ }
178773
+ }
178774
+ return { prereqOrder: livePrereqOrder };
178775
+ }
178712
178776
  async function walkPrerequisiteChain(skill, unboundParams, prerequisiteOrder, baseParams, queryIntent, projection, options, steps) {
178713
178777
  const resolved = {};
178714
178778
  const executed = new Map;
@@ -179989,13 +180053,65 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
179989
180053
  };
179990
180054
  const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
179991
180055
  let chainBound = {};
179992
- const prereqOrder = dagPlan?.prerequisite_order ?? [];
180056
+ let prereqOrder = dagPlan?.prerequisite_order ?? [];
180057
+ let replayedCompositeId;
180058
+ if (stillUnbound.length > 0) {
180059
+ const replayDomain = skill.domain ?? (() => {
180060
+ try {
180061
+ return new URL(candidate.endpoint.url_template).hostname;
180062
+ } catch {
180063
+ return "";
180064
+ }
180065
+ })();
180066
+ const persisted = readComposite(replayDomain, candidate.endpoint.endpoint_id);
180067
+ const decision = planPrereqOrder(prereqOrder, persisted, (id) => {
180068
+ const ep = skill.endpoints.find((e) => e.endpoint_id === id);
180069
+ return ep != null && canAutoExecuteEndpoint(ep);
180070
+ });
180071
+ prereqOrder = decision.prereqOrder;
180072
+ replayedCompositeId = decision.replayedCompositeId;
180073
+ if (replayedCompositeId) {
180074
+ console.log(`[chain] composite replay → ${candidate.endpoint.endpoint_id} (${replayedCompositeId})`);
180075
+ }
180076
+ }
179993
180077
  if (stillUnbound.length > 0 && prereqOrder.length > 0) {
179994
180078
  const chainSteps = [];
179995
180079
  chainBound = await walkPrerequisiteChain(skill, stillUnbound, prereqOrder, { ...templateDefaults, ...endpointParams, ...syncInferred, ...searchOverrides }, queryIntent, projection, { ...options, intent: queryIntent, contextUrl: context?.url }, chainSteps);
179996
180080
  if (chainSteps.length > 0) {
180081
+ const compositeEdges = buildCompositeEdges(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound));
180082
+ const compositeDomain = skill.domain ?? (() => {
180083
+ try {
180084
+ return new URL(candidate.endpoint.url_template).hostname;
180085
+ } catch {
180086
+ return "";
180087
+ }
180088
+ })();
180089
+ const compositeIntentSig = (queryIntent ?? "").trim().toLowerCase();
180090
+ const compositeId = compositeAddress(compositeDomain, candidate.endpoint.endpoint_id, chainSteps, compositeEdges);
180091
+ const composite = {
180092
+ composite_id: compositeId,
180093
+ target: candidate.endpoint.endpoint_id,
180094
+ steps: chainSteps,
180095
+ edges: compositeEdges,
180096
+ mode: replayedCompositeId === compositeId ? "composite_replay" : "composite_walk"
180097
+ };
179997
180098
  decisionTrace.prerequisite_chain = chainSteps;
179998
- console.log(`[chain] walked ${chainSteps.length} prerequisite(s): ` + chainSteps.map((s) => `${s.endpoint_id}${s.ok ? "✓" : "✗"}→[${s.yielded.join(",")}]`).join(" "));
180099
+ decisionTrace.composite = composite;
180100
+ if (chainSteps.every((s) => s.ok) && canAutoExecuteEndpoint(candidate.endpoint)) {
180101
+ const persistedPath = writeComposite({
180102
+ composite_id: compositeId,
180103
+ intent_signature: compositeIntentSig,
180104
+ domain: compositeDomain,
180105
+ target: composite.target,
180106
+ steps: chainSteps,
180107
+ edges: compositeEdges,
180108
+ created_at: new Date().toISOString()
180109
+ });
180110
+ if (persistedPath) {
180111
+ decisionTrace.composite_persisted = compositeId;
180112
+ }
180113
+ }
180114
+ console.log(`[chain] composite → ${composite.target} via ` + composite.edges.map((e) => `${e.from}.${e.binding}`).join(" + ") || "(no edges)");
179999
180115
  }
180000
180116
  }
180001
180117
  const chainResolvedKeys = new Set(Object.keys(chainBound));
package/runtime/mcp.js CHANGED
@@ -124931,6 +124931,49 @@ function writeSkillSnapshot(cacheKey2, skill) {
124931
124931
  return;
124932
124932
  }
124933
124933
  }
124934
+ function compositeSnapshotDir() {
124935
+ return process.env.UNBROWSE_COMPOSITE_DIR ?? join26(process.env.HOME ?? "/tmp", ".unbrowse", "composites");
124936
+ }
124937
+ function compositeAddress(domain, target, steps, edges) {
124938
+ const canonical2 = [
124939
+ `domain:${domain}`,
124940
+ `target:${target}`,
124941
+ `steps:${steps.map((s) => s.endpoint_id).join(">")}`,
124942
+ `edges:${edges.map((e) => `${e.from}.${e.binding}->${e.to}`).sort().join("|")}`
124943
+ ].join("::");
124944
+ return `composite:${createHash9("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
124945
+ }
124946
+ function compositeLookupKey(domain, target) {
124947
+ const canonical2 = `domain:${domain}::target:${target}`;
124948
+ return `lookup:${createHash9("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
124949
+ }
124950
+ function compositeFilePath(lookupKey) {
124951
+ return join26(compositeSnapshotDir(), `${lookupKey.replace(/[^a-z0-9]/gi, "_")}.json`);
124952
+ }
124953
+ function writeComposite(c) {
124954
+ if (process.env.UNBROWSE_STATELESS === "1")
124955
+ return;
124956
+ if (process.env.UNBROWSE_LOCAL_CACHES !== "1")
124957
+ return;
124958
+ try {
124959
+ mkdirSync15(compositeSnapshotDir(), { recursive: true });
124960
+ const target = compositeFilePath(compositeLookupKey(c.domain, c.target));
124961
+ writeFileSync13(target, JSON.stringify(c), "utf-8");
124962
+ return target;
124963
+ } catch {
124964
+ return;
124965
+ }
124966
+ }
124967
+ function readComposite(domain, target) {
124968
+ try {
124969
+ const path8 = compositeFilePath(compositeLookupKey(domain, target));
124970
+ if (!existsSync23(path8))
124971
+ return;
124972
+ return JSON.parse(readFileSync18(path8, "utf-8"));
124973
+ } catch {
124974
+ return;
124975
+ }
124976
+ }
124934
124977
  function hasSearchBindings(endpoint) {
124935
124978
  const haystack = JSON.stringify({
124936
124979
  url: endpoint.url_template,
@@ -125987,6 +126030,27 @@ function extractSearchTermsFromIntent(intent) {
125987
126030
  }
125988
126031
  return terms || null;
125989
126032
  }
126033
+ function buildCompositeEdges(target, steps, boundKeys) {
126034
+ return boundKeys.map((binding) => {
126035
+ const from = steps.find((s) => s.yielded.includes(binding));
126036
+ return from ? { from: from.endpoint_id, binding, to: target } : null;
126037
+ }).filter((e) => e !== null);
126038
+ }
126039
+ function planPrereqOrder(livePrereqOrder, persisted, isReplayable) {
126040
+ if (persisted && persisted.steps.length > 0) {
126041
+ const recordedOrder = persisted.steps.map((s) => s.endpoint_id);
126042
+ if (recordedOrder.every((id) => isReplayable(id))) {
126043
+ return {
126044
+ prereqOrder: [
126045
+ ...recordedOrder,
126046
+ ...livePrereqOrder.filter((p) => !recordedOrder.includes(p))
126047
+ ],
126048
+ replayedCompositeId: persisted.composite_id
126049
+ };
126050
+ }
126051
+ }
126052
+ return { prereqOrder: livePrereqOrder };
126053
+ }
125990
126054
  async function walkPrerequisiteChain(skill, unboundParams, prerequisiteOrder, baseParams, queryIntent, projection, options, steps) {
125991
126055
  const resolved = {};
125992
126056
  const executed = new Map;
@@ -127267,13 +127331,65 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
127267
127331
  };
127268
127332
  const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
127269
127333
  let chainBound = {};
127270
- const prereqOrder = dagPlan?.prerequisite_order ?? [];
127334
+ let prereqOrder = dagPlan?.prerequisite_order ?? [];
127335
+ let replayedCompositeId;
127336
+ if (stillUnbound.length > 0) {
127337
+ const replayDomain = skill.domain ?? (() => {
127338
+ try {
127339
+ return new URL(candidate.endpoint.url_template).hostname;
127340
+ } catch {
127341
+ return "";
127342
+ }
127343
+ })();
127344
+ const persisted = readComposite(replayDomain, candidate.endpoint.endpoint_id);
127345
+ const decision = planPrereqOrder(prereqOrder, persisted, (id) => {
127346
+ const ep = skill.endpoints.find((e) => e.endpoint_id === id);
127347
+ return ep != null && canAutoExecuteEndpoint(ep);
127348
+ });
127349
+ prereqOrder = decision.prereqOrder;
127350
+ replayedCompositeId = decision.replayedCompositeId;
127351
+ if (replayedCompositeId) {
127352
+ console.log(`[chain] composite replay → ${candidate.endpoint.endpoint_id} (${replayedCompositeId})`);
127353
+ }
127354
+ }
127271
127355
  if (stillUnbound.length > 0 && prereqOrder.length > 0) {
127272
127356
  const chainSteps = [];
127273
127357
  chainBound = await walkPrerequisiteChain(skill, stillUnbound, prereqOrder, { ...templateDefaults, ...endpointParams, ...syncInferred, ...searchOverrides }, queryIntent, projection, { ...options, intent: queryIntent, contextUrl: context?.url }, chainSteps);
127274
127358
  if (chainSteps.length > 0) {
127359
+ const compositeEdges = buildCompositeEdges(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound));
127360
+ const compositeDomain = skill.domain ?? (() => {
127361
+ try {
127362
+ return new URL(candidate.endpoint.url_template).hostname;
127363
+ } catch {
127364
+ return "";
127365
+ }
127366
+ })();
127367
+ const compositeIntentSig = (queryIntent ?? "").trim().toLowerCase();
127368
+ const compositeId = compositeAddress(compositeDomain, candidate.endpoint.endpoint_id, chainSteps, compositeEdges);
127369
+ const composite = {
127370
+ composite_id: compositeId,
127371
+ target: candidate.endpoint.endpoint_id,
127372
+ steps: chainSteps,
127373
+ edges: compositeEdges,
127374
+ mode: replayedCompositeId === compositeId ? "composite_replay" : "composite_walk"
127375
+ };
127275
127376
  decisionTrace.prerequisite_chain = chainSteps;
127276
- console.log(`[chain] walked ${chainSteps.length} prerequisite(s): ` + chainSteps.map((s) => `${s.endpoint_id}${s.ok ? "✓" : "✗"}→[${s.yielded.join(",")}]`).join(" "));
127377
+ decisionTrace.composite = composite;
127378
+ if (chainSteps.every((s) => s.ok) && canAutoExecuteEndpoint(candidate.endpoint)) {
127379
+ const persistedPath = writeComposite({
127380
+ composite_id: compositeId,
127381
+ intent_signature: compositeIntentSig,
127382
+ domain: compositeDomain,
127383
+ target: composite.target,
127384
+ steps: chainSteps,
127385
+ edges: compositeEdges,
127386
+ created_at: new Date().toISOString()
127387
+ });
127388
+ if (persistedPath) {
127389
+ decisionTrace.composite_persisted = compositeId;
127390
+ }
127391
+ }
127392
+ console.log(`[chain] composite → ${composite.target} via ` + composite.edges.map((e) => `${e.from}.${e.binding}`).join(" + ") || "(no edges)");
127277
127393
  }
127278
127394
  }
127279
127395
  const chainResolvedKeys = new Set(Object.keys(chainBound));
@@ -191623,6 +191739,7 @@ var init_flex_pay2 = __esm(() => {
191623
191739
  var exports_orchestrator = {};
191624
191740
  __export(exports_orchestrator, {
191625
191741
  writeSkillSnapshot: () => writeSkillSnapshot2,
191742
+ writeComposite: () => writeComposite2,
191626
191743
  tryDirectJsonFetch: () => tryDirectJsonFetch2,
191627
191744
  summarizeSchema: () => summarizeSchema2,
191628
191745
  snapshotPathForCacheKey: () => snapshotPathForCacheKey2,
@@ -191636,10 +191753,12 @@ __export(exports_orchestrator, {
191636
191753
  resolveEndpointTemplateBindings: () => resolveEndpointTemplateBindings2,
191637
191754
  resolveAndExecute: () => resolveAndExecute2,
191638
191755
  readSkillSnapshot: () => readSkillSnapshot2,
191756
+ readComposite: () => readComposite2,
191639
191757
  pruneLocalCacheEntriesForSkill: () => pruneLocalCacheEntriesForSkill2,
191640
191758
  promoteExplicitExecution: () => promoteExplicitExecution2,
191641
191759
  probeLooksLikeFetchableHtmlDocument: () => probeLooksLikeFetchableHtmlDocument2,
191642
191760
  probeLooksLikeDirectJsonApi: () => probeLooksLikeDirectJsonApi2,
191761
+ planPrereqOrder: () => planPrereqOrder2,
191643
191762
  pickPreferredSkillSnapshot: () => pickPreferredSkillSnapshot2,
191644
191763
  persistDomainCache: () => persistDomainCache2,
191645
191764
  marketplaceSkillMatchesContext: () => marketplaceSkillMatchesContext2,
@@ -191656,8 +191775,11 @@ __export(exports_orchestrator, {
191656
191775
  extractSampleValues: () => extractSampleValues2,
191657
191776
  extractLiteralSearchTermsFromIntent: () => extractLiteralSearchTermsFromIntent2,
191658
191777
  domainSkillCache: () => domainSkillCache2,
191778
+ compositeLookupKey: () => compositeLookupKey2,
191779
+ compositeAddress: () => compositeAddress2,
191659
191780
  chooseBestRouteCacheCandidate: () => chooseBestRouteCacheCandidate2,
191660
191781
  buildResolveCacheKey: () => buildResolveCacheKey2,
191782
+ buildCompositeEdges: () => buildCompositeEdges2,
191661
191783
  assessLocalExecutionResult: () => assessLocalExecutionResult2
191662
191784
  });
191663
191785
  import { existsSync as existsSync50, writeFileSync as writeFileSync26, readFileSync as readFileSync39, mkdirSync as mkdirSync30, readdirSync as readdirSync17 } from "node:fs";
@@ -191834,6 +191956,49 @@ function writeSkillSnapshot2(cacheKey2, skill) {
191834
191956
  return;
191835
191957
  }
191836
191958
  }
191959
+ function compositeSnapshotDir2() {
191960
+ return process.env.UNBROWSE_COMPOSITE_DIR ?? join58(process.env.HOME ?? "/tmp", ".unbrowse", "composites");
191961
+ }
191962
+ function compositeAddress2(domain, target, steps, edges) {
191963
+ const canonical2 = [
191964
+ `domain:${domain}`,
191965
+ `target:${target}`,
191966
+ `steps:${steps.map((s) => s.endpoint_id).join(">")}`,
191967
+ `edges:${edges.map((e) => `${e.from}.${e.binding}->${e.to}`).sort().join("|")}`
191968
+ ].join("::");
191969
+ return `composite:${createHash40("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
191970
+ }
191971
+ function compositeLookupKey2(domain, target) {
191972
+ const canonical2 = `domain:${domain}::target:${target}`;
191973
+ return `lookup:${createHash40("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
191974
+ }
191975
+ function compositeFilePath2(lookupKey2) {
191976
+ return join58(compositeSnapshotDir2(), `${lookupKey2.replace(/[^a-z0-9]/gi, "_")}.json`);
191977
+ }
191978
+ function writeComposite2(c) {
191979
+ if (process.env.UNBROWSE_STATELESS === "1")
191980
+ return;
191981
+ if (process.env.UNBROWSE_LOCAL_CACHES !== "1")
191982
+ return;
191983
+ try {
191984
+ mkdirSync30(compositeSnapshotDir2(), { recursive: true });
191985
+ const target = compositeFilePath2(compositeLookupKey2(c.domain, c.target));
191986
+ writeFileSync26(target, JSON.stringify(c), "utf-8");
191987
+ return target;
191988
+ } catch {
191989
+ return;
191990
+ }
191991
+ }
191992
+ function readComposite2(domain, target) {
191993
+ try {
191994
+ const path23 = compositeFilePath2(compositeLookupKey2(domain, target));
191995
+ if (!existsSync50(path23))
191996
+ return;
191997
+ return JSON.parse(readFileSync39(path23, "utf-8"));
191998
+ } catch {
191999
+ return;
192000
+ }
192001
+ }
191837
192002
  function hasSearchBindings2(endpoint) {
191838
192003
  const haystack = JSON.stringify({
191839
192004
  url: endpoint.url_template,
@@ -192895,6 +193060,27 @@ function extractSearchTermsFromIntent2(intent) {
192895
193060
  }
192896
193061
  return terms || null;
192897
193062
  }
193063
+ function buildCompositeEdges2(target, steps, boundKeys) {
193064
+ return boundKeys.map((binding) => {
193065
+ const from = steps.find((s) => s.yielded.includes(binding));
193066
+ return from ? { from: from.endpoint_id, binding, to: target } : null;
193067
+ }).filter((e) => e !== null);
193068
+ }
193069
+ function planPrereqOrder2(livePrereqOrder, persisted, isReplayable) {
193070
+ if (persisted && persisted.steps.length > 0) {
193071
+ const recordedOrder = persisted.steps.map((s) => s.endpoint_id);
193072
+ if (recordedOrder.every((id) => isReplayable(id))) {
193073
+ return {
193074
+ prereqOrder: [
193075
+ ...recordedOrder,
193076
+ ...livePrereqOrder.filter((p) => !recordedOrder.includes(p))
193077
+ ],
193078
+ replayedCompositeId: persisted.composite_id
193079
+ };
193080
+ }
193081
+ }
193082
+ return { prereqOrder: livePrereqOrder };
193083
+ }
192898
193084
  async function walkPrerequisiteChain2(skill, unboundParams, prerequisiteOrder, baseParams, queryIntent, projection, options, steps) {
192899
193085
  const resolved = {};
192900
193086
  const executed = new Map;
@@ -194175,13 +194361,65 @@ async function resolveAndExecute2(intent, params = {}, context, projection, opti
194175
194361
  };
194176
194362
  const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
194177
194363
  let chainBound = {};
194178
- const prereqOrder = dagPlan?.prerequisite_order ?? [];
194364
+ let prereqOrder = dagPlan?.prerequisite_order ?? [];
194365
+ let replayedCompositeId;
194366
+ if (stillUnbound.length > 0) {
194367
+ const replayDomain = skill.domain ?? (() => {
194368
+ try {
194369
+ return new URL(candidate.endpoint.url_template).hostname;
194370
+ } catch {
194371
+ return "";
194372
+ }
194373
+ })();
194374
+ const persisted = readComposite2(replayDomain, candidate.endpoint.endpoint_id);
194375
+ const decision = planPrereqOrder2(prereqOrder, persisted, (id) => {
194376
+ const ep = skill.endpoints.find((e) => e.endpoint_id === id);
194377
+ return ep != null && canAutoExecuteEndpoint(ep);
194378
+ });
194379
+ prereqOrder = decision.prereqOrder;
194380
+ replayedCompositeId = decision.replayedCompositeId;
194381
+ if (replayedCompositeId) {
194382
+ console.log(`[chain] composite replay → ${candidate.endpoint.endpoint_id} (${replayedCompositeId})`);
194383
+ }
194384
+ }
194179
194385
  if (stillUnbound.length > 0 && prereqOrder.length > 0) {
194180
194386
  const chainSteps = [];
194181
194387
  chainBound = await walkPrerequisiteChain2(skill, stillUnbound, prereqOrder, { ...templateDefaults, ...endpointParams, ...syncInferred, ...searchOverrides }, queryIntent, projection, { ...options, intent: queryIntent, contextUrl: context?.url }, chainSteps);
194182
194388
  if (chainSteps.length > 0) {
194389
+ const compositeEdges = buildCompositeEdges2(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound));
194390
+ const compositeDomain = skill.domain ?? (() => {
194391
+ try {
194392
+ return new URL(candidate.endpoint.url_template).hostname;
194393
+ } catch {
194394
+ return "";
194395
+ }
194396
+ })();
194397
+ const compositeIntentSig = (queryIntent ?? "").trim().toLowerCase();
194398
+ const compositeId = compositeAddress2(compositeDomain, candidate.endpoint.endpoint_id, chainSteps, compositeEdges);
194399
+ const composite = {
194400
+ composite_id: compositeId,
194401
+ target: candidate.endpoint.endpoint_id,
194402
+ steps: chainSteps,
194403
+ edges: compositeEdges,
194404
+ mode: replayedCompositeId === compositeId ? "composite_replay" : "composite_walk"
194405
+ };
194183
194406
  decisionTrace.prerequisite_chain = chainSteps;
194184
- console.log(`[chain] walked ${chainSteps.length} prerequisite(s): ` + chainSteps.map((s) => `${s.endpoint_id}${s.ok ? "✓" : "✗"}→[${s.yielded.join(",")}]`).join(" "));
194407
+ decisionTrace.composite = composite;
194408
+ if (chainSteps.every((s) => s.ok) && canAutoExecuteEndpoint(candidate.endpoint)) {
194409
+ const persistedPath = writeComposite2({
194410
+ composite_id: compositeId,
194411
+ intent_signature: compositeIntentSig,
194412
+ domain: compositeDomain,
194413
+ target: composite.target,
194414
+ steps: chainSteps,
194415
+ edges: compositeEdges,
194416
+ created_at: new Date().toISOString()
194417
+ });
194418
+ if (persistedPath) {
194419
+ decisionTrace.composite_persisted = compositeId;
194420
+ }
194421
+ }
194422
+ console.log(`[chain] composite → ${composite.target} via ` + composite.edges.map((e) => `${e.from}.${e.binding}`).join(" + ") || "(no edges)");
194185
194423
  }
194186
194424
  }
194187
194425
  const chainResolvedKeys = new Set(Object.keys(chainBound));
Binary file
@@ -2,7 +2,7 @@
2
2
  "repo_url": "https://github.com/justrach/kuri.git",
3
3
  "branch": "adding-extensions",
4
4
  "source_sha": "8938f89f3d0c032bd19c59db0de4eadca18a1800",
5
- "built_at": "2026-06-14T01:52:08.995Z",
5
+ "built_at": "2026-06-14T04:09:29.728Z",
6
6
  "binaries": {
7
7
  "darwin-arm64": {
8
8
  "zig_target": "aarch64-macos",
@@ -21,33 +21,33 @@
21
21
  },
22
22
  "linux-x64": {
23
23
  "zig_target": "x86_64-linux",
24
- "sha256": "9ed8f304b849f6d7056776763a7e8c0978da044bf1acf4cb05c7e6e9889f1d0a"
24
+ "sha256": "287c37dd7c9409214b638ba588e26d160fd5078d7b50c0e8673eec64086692c1"
25
25
  },
26
26
  "win-x64": {
27
27
  "zig_target": "x86_64-windows-gnu",
28
- "sha256": "3493a6571fcf57c9e189f6f59f5f75c4ca2dd5fb3f5d66216855ed3d88dcb6f1"
28
+ "sha256": "12374edb4547931d6efe8c187dc91c92984f0305fe7cc32276a9a7eddda17808"
29
29
  }
30
30
  },
31
31
  "ffi": {
32
32
  "darwin-arm64": {
33
33
  "zig_target": "aarch64-macos",
34
34
  "lib": "libkuri_ffi.dylib",
35
- "sha256": "5551b3f8aa23c572370b3691c4cf127a697c4e92f5e9f49b6f6f6a0e11f9d402"
35
+ "sha256": "e4d25d11504e9f366ff4fca192dd5bfc7d3475a483157541816ff6b75329b498"
36
36
  },
37
37
  "darwin-x64": {
38
38
  "zig_target": "x86_64-macos",
39
39
  "lib": "libkuri_ffi.dylib",
40
- "sha256": "f93c65b07f22c89ec5893aa3a35e1faf0e19f9a5c16eeb2fd5d22397c301a631"
40
+ "sha256": "c3858a4d4d9f2a713c19835c3f2de15687aaadb6a8f091455920708eda84d069"
41
41
  },
42
42
  "linux-arm64": {
43
43
  "zig_target": "aarch64-linux",
44
44
  "lib": "libkuri_ffi.so",
45
- "sha256": "97184ac3f943c9d493044bb26acd73b868c8f9f21aee0331b82b62d0cbdb147f"
45
+ "sha256": "21418a3144796ddcec50721f6a2a6ee86c9518e4515cfdd45a644ee026bc4433"
46
46
  },
47
47
  "linux-x64": {
48
48
  "zig_target": "x86_64-linux",
49
49
  "lib": "libkuri_ffi.so",
50
- "sha256": "f47ba1daf8d61b5c9e83bacde8d86598c0283d69294365dda9a54429f2ad182a"
50
+ "sha256": "58c61c8bcaaa9d49e78077732d5e4493cad9d684853eb6f501da810d5d3550a4"
51
51
  }
52
52
  }
53
53
  }
Binary file