unbrowse 9.0.2 → 9.0.4

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.2",
3
+ "version": "9.0.4",
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,59 @@ 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
+ }
177699
+ function findCompositeInSkill(skill, target) {
177700
+ return skill?.composites?.find((c) => c.target === target && c.steps.length > 0);
177701
+ }
177702
+ function attachCompositeToSkill(skill, composite) {
177703
+ const list = skill.composites ?? (skill.composites = []);
177704
+ if (list.some((c) => c.composite_id === composite.composite_id))
177705
+ return false;
177706
+ list.push(composite);
177707
+ return true;
177708
+ }
177656
177709
  function hasSearchBindings(endpoint) {
177657
177710
  const haystack = JSON.stringify({
177658
177711
  url: endpoint.url_template,
@@ -178715,6 +178768,21 @@ function buildCompositeEdges(target, steps, boundKeys) {
178715
178768
  return from ? { from: from.endpoint_id, binding, to: target } : null;
178716
178769
  }).filter((e) => e !== null);
178717
178770
  }
178771
+ function planPrereqOrder(livePrereqOrder, persisted, isReplayable) {
178772
+ if (persisted && persisted.steps.length > 0) {
178773
+ const recordedOrder = persisted.steps.map((s) => s.endpoint_id);
178774
+ if (recordedOrder.every((id) => isReplayable(id))) {
178775
+ return {
178776
+ prereqOrder: [
178777
+ ...recordedOrder,
178778
+ ...livePrereqOrder.filter((p) => !recordedOrder.includes(p))
178779
+ ],
178780
+ replayedCompositeId: persisted.composite_id
178781
+ };
178782
+ }
178783
+ }
178784
+ return { prereqOrder: livePrereqOrder };
178785
+ }
178718
178786
  async function walkPrerequisiteChain(skill, unboundParams, prerequisiteOrder, baseParams, queryIntent, projection, options, steps) {
178719
178787
  const resolved = {};
178720
178788
  const executed = new Map;
@@ -179995,18 +180063,69 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
179995
180063
  };
179996
180064
  const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
179997
180065
  let chainBound = {};
179998
- const prereqOrder = dagPlan?.prerequisite_order ?? [];
180066
+ let prereqOrder = dagPlan?.prerequisite_order ?? [];
180067
+ let replayedCompositeId;
180068
+ if (stillUnbound.length > 0) {
180069
+ const replayDomain = skill.domain ?? (() => {
180070
+ try {
180071
+ return new URL(candidate.endpoint.url_template).hostname;
180072
+ } catch {
180073
+ return "";
180074
+ }
180075
+ })();
180076
+ const persisted = findCompositeInSkill(skill, candidate.endpoint.endpoint_id) ?? readComposite(replayDomain, candidate.endpoint.endpoint_id);
180077
+ const decision = planPrereqOrder(prereqOrder, persisted, (id) => {
180078
+ const ep = skill.endpoints.find((e) => e.endpoint_id === id);
180079
+ return ep != null && canAutoExecuteEndpoint(ep);
180080
+ });
180081
+ prereqOrder = decision.prereqOrder;
180082
+ replayedCompositeId = decision.replayedCompositeId;
180083
+ if (replayedCompositeId) {
180084
+ console.log(`[chain] composite replay → ${candidate.endpoint.endpoint_id} (${replayedCompositeId})`);
180085
+ }
180086
+ }
179999
180087
  if (stillUnbound.length > 0 && prereqOrder.length > 0) {
180000
180088
  const chainSteps = [];
180001
180089
  chainBound = await walkPrerequisiteChain(skill, stillUnbound, prereqOrder, { ...templateDefaults, ...endpointParams, ...syncInferred, ...searchOverrides }, queryIntent, projection, { ...options, intent: queryIntent, contextUrl: context?.url }, chainSteps);
180002
180090
  if (chainSteps.length > 0) {
180091
+ const compositeEdges = buildCompositeEdges(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound));
180092
+ const compositeDomain = skill.domain ?? (() => {
180093
+ try {
180094
+ return new URL(candidate.endpoint.url_template).hostname;
180095
+ } catch {
180096
+ return "";
180097
+ }
180098
+ })();
180099
+ const compositeIntentSig = (queryIntent ?? "").trim().toLowerCase();
180100
+ const compositeId = compositeAddress(compositeDomain, candidate.endpoint.endpoint_id, chainSteps, compositeEdges);
180003
180101
  const composite = {
180102
+ composite_id: compositeId,
180004
180103
  target: candidate.endpoint.endpoint_id,
180005
180104
  steps: chainSteps,
180006
- edges: buildCompositeEdges(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound))
180105
+ edges: compositeEdges,
180106
+ mode: replayedCompositeId === compositeId ? "composite_replay" : "composite_walk"
180007
180107
  };
180008
180108
  decisionTrace.prerequisite_chain = chainSteps;
180009
180109
  decisionTrace.composite = composite;
180110
+ if (chainSteps.every((s) => s.ok) && canAutoExecuteEndpoint(candidate.endpoint)) {
180111
+ const descriptor = {
180112
+ composite_id: compositeId,
180113
+ intent_signature: compositeIntentSig,
180114
+ domain: compositeDomain,
180115
+ target: composite.target,
180116
+ steps: chainSteps,
180117
+ edges: compositeEdges,
180118
+ created_at: new Date().toISOString()
180119
+ };
180120
+ const composedNewlyAttached = attachCompositeToSkill(skill, descriptor);
180121
+ if (composedNewlyAttached && skill.skill_id) {
180122
+ queuePassiveSkillPublish(skill).catch(() => {});
180123
+ }
180124
+ const persistedPath = writeComposite(descriptor);
180125
+ if (persistedPath) {
180126
+ decisionTrace.composite_persisted = compositeId;
180127
+ }
180128
+ }
180010
180129
  console.log(`[chain] composite → ${composite.target} via ` + composite.edges.map((e) => `${e.from}.${e.binding}`).join(" + ") || "(no edges)");
180011
180130
  }
180012
180131
  }
package/runtime/mcp.js CHANGED
@@ -124931,6 +124931,59 @@ 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
+ }
124977
+ function findCompositeInSkill(skill, target) {
124978
+ return skill?.composites?.find((c) => c.target === target && c.steps.length > 0);
124979
+ }
124980
+ function attachCompositeToSkill(skill, composite) {
124981
+ const list = skill.composites ?? (skill.composites = []);
124982
+ if (list.some((c) => c.composite_id === composite.composite_id))
124983
+ return false;
124984
+ list.push(composite);
124985
+ return true;
124986
+ }
124934
124987
  function hasSearchBindings(endpoint) {
124935
124988
  const haystack = JSON.stringify({
124936
124989
  url: endpoint.url_template,
@@ -125993,6 +126046,21 @@ function buildCompositeEdges(target, steps, boundKeys) {
125993
126046
  return from ? { from: from.endpoint_id, binding, to: target } : null;
125994
126047
  }).filter((e) => e !== null);
125995
126048
  }
126049
+ function planPrereqOrder(livePrereqOrder, persisted, isReplayable) {
126050
+ if (persisted && persisted.steps.length > 0) {
126051
+ const recordedOrder = persisted.steps.map((s) => s.endpoint_id);
126052
+ if (recordedOrder.every((id) => isReplayable(id))) {
126053
+ return {
126054
+ prereqOrder: [
126055
+ ...recordedOrder,
126056
+ ...livePrereqOrder.filter((p) => !recordedOrder.includes(p))
126057
+ ],
126058
+ replayedCompositeId: persisted.composite_id
126059
+ };
126060
+ }
126061
+ }
126062
+ return { prereqOrder: livePrereqOrder };
126063
+ }
125996
126064
  async function walkPrerequisiteChain(skill, unboundParams, prerequisiteOrder, baseParams, queryIntent, projection, options, steps) {
125997
126065
  const resolved = {};
125998
126066
  const executed = new Map;
@@ -127273,18 +127341,69 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
127273
127341
  };
127274
127342
  const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
127275
127343
  let chainBound = {};
127276
- const prereqOrder = dagPlan?.prerequisite_order ?? [];
127344
+ let prereqOrder = dagPlan?.prerequisite_order ?? [];
127345
+ let replayedCompositeId;
127346
+ if (stillUnbound.length > 0) {
127347
+ const replayDomain = skill.domain ?? (() => {
127348
+ try {
127349
+ return new URL(candidate.endpoint.url_template).hostname;
127350
+ } catch {
127351
+ return "";
127352
+ }
127353
+ })();
127354
+ const persisted = findCompositeInSkill(skill, candidate.endpoint.endpoint_id) ?? readComposite(replayDomain, candidate.endpoint.endpoint_id);
127355
+ const decision = planPrereqOrder(prereqOrder, persisted, (id) => {
127356
+ const ep = skill.endpoints.find((e) => e.endpoint_id === id);
127357
+ return ep != null && canAutoExecuteEndpoint(ep);
127358
+ });
127359
+ prereqOrder = decision.prereqOrder;
127360
+ replayedCompositeId = decision.replayedCompositeId;
127361
+ if (replayedCompositeId) {
127362
+ console.log(`[chain] composite replay → ${candidate.endpoint.endpoint_id} (${replayedCompositeId})`);
127363
+ }
127364
+ }
127277
127365
  if (stillUnbound.length > 0 && prereqOrder.length > 0) {
127278
127366
  const chainSteps = [];
127279
127367
  chainBound = await walkPrerequisiteChain(skill, stillUnbound, prereqOrder, { ...templateDefaults, ...endpointParams, ...syncInferred, ...searchOverrides }, queryIntent, projection, { ...options, intent: queryIntent, contextUrl: context?.url }, chainSteps);
127280
127368
  if (chainSteps.length > 0) {
127369
+ const compositeEdges = buildCompositeEdges(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound));
127370
+ const compositeDomain = skill.domain ?? (() => {
127371
+ try {
127372
+ return new URL(candidate.endpoint.url_template).hostname;
127373
+ } catch {
127374
+ return "";
127375
+ }
127376
+ })();
127377
+ const compositeIntentSig = (queryIntent ?? "").trim().toLowerCase();
127378
+ const compositeId = compositeAddress(compositeDomain, candidate.endpoint.endpoint_id, chainSteps, compositeEdges);
127281
127379
  const composite = {
127380
+ composite_id: compositeId,
127282
127381
  target: candidate.endpoint.endpoint_id,
127283
127382
  steps: chainSteps,
127284
- edges: buildCompositeEdges(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound))
127383
+ edges: compositeEdges,
127384
+ mode: replayedCompositeId === compositeId ? "composite_replay" : "composite_walk"
127285
127385
  };
127286
127386
  decisionTrace.prerequisite_chain = chainSteps;
127287
127387
  decisionTrace.composite = composite;
127388
+ if (chainSteps.every((s) => s.ok) && canAutoExecuteEndpoint(candidate.endpoint)) {
127389
+ const descriptor = {
127390
+ composite_id: compositeId,
127391
+ intent_signature: compositeIntentSig,
127392
+ domain: compositeDomain,
127393
+ target: composite.target,
127394
+ steps: chainSteps,
127395
+ edges: compositeEdges,
127396
+ created_at: new Date().toISOString()
127397
+ };
127398
+ const composedNewlyAttached = attachCompositeToSkill(skill, descriptor);
127399
+ if (composedNewlyAttached && skill.skill_id) {
127400
+ queuePassiveSkillPublish(skill).catch(() => {});
127401
+ }
127402
+ const persistedPath = writeComposite(descriptor);
127403
+ if (persistedPath) {
127404
+ decisionTrace.composite_persisted = compositeId;
127405
+ }
127406
+ }
127288
127407
  console.log(`[chain] composite → ${composite.target} via ` + composite.edges.map((e) => `${e.from}.${e.binding}`).join(" + ") || "(no edges)");
127289
127408
  }
127290
127409
  }
@@ -191635,6 +191754,7 @@ var init_flex_pay2 = __esm(() => {
191635
191754
  var exports_orchestrator = {};
191636
191755
  __export(exports_orchestrator, {
191637
191756
  writeSkillSnapshot: () => writeSkillSnapshot2,
191757
+ writeComposite: () => writeComposite2,
191638
191758
  tryDirectJsonFetch: () => tryDirectJsonFetch2,
191639
191759
  summarizeSchema: () => summarizeSchema2,
191640
191760
  snapshotPathForCacheKey: () => snapshotPathForCacheKey2,
@@ -191648,10 +191768,12 @@ __export(exports_orchestrator, {
191648
191768
  resolveEndpointTemplateBindings: () => resolveEndpointTemplateBindings2,
191649
191769
  resolveAndExecute: () => resolveAndExecute2,
191650
191770
  readSkillSnapshot: () => readSkillSnapshot2,
191771
+ readComposite: () => readComposite2,
191651
191772
  pruneLocalCacheEntriesForSkill: () => pruneLocalCacheEntriesForSkill2,
191652
191773
  promoteExplicitExecution: () => promoteExplicitExecution2,
191653
191774
  probeLooksLikeFetchableHtmlDocument: () => probeLooksLikeFetchableHtmlDocument2,
191654
191775
  probeLooksLikeDirectJsonApi: () => probeLooksLikeDirectJsonApi2,
191776
+ planPrereqOrder: () => planPrereqOrder2,
191655
191777
  pickPreferredSkillSnapshot: () => pickPreferredSkillSnapshot2,
191656
191778
  persistDomainCache: () => persistDomainCache2,
191657
191779
  marketplaceSkillMatchesContext: () => marketplaceSkillMatchesContext2,
@@ -191664,13 +191786,17 @@ __export(exports_orchestrator, {
191664
191786
  getDomainReuseKey: () => getDomainReuseKey2,
191665
191787
  generateLocalDescription: () => generateLocalDescription2,
191666
191788
  findEndpointInSkillHistory: () => findEndpointInSkillHistory2,
191789
+ findCompositeInSkill: () => findCompositeInSkill2,
191667
191790
  extractSearchTermsFromIntent: () => extractSearchTermsFromIntent2,
191668
191791
  extractSampleValues: () => extractSampleValues2,
191669
191792
  extractLiteralSearchTermsFromIntent: () => extractLiteralSearchTermsFromIntent2,
191670
191793
  domainSkillCache: () => domainSkillCache2,
191794
+ compositeLookupKey: () => compositeLookupKey2,
191795
+ compositeAddress: () => compositeAddress2,
191671
191796
  chooseBestRouteCacheCandidate: () => chooseBestRouteCacheCandidate2,
191672
191797
  buildResolveCacheKey: () => buildResolveCacheKey2,
191673
191798
  buildCompositeEdges: () => buildCompositeEdges2,
191799
+ attachCompositeToSkill: () => attachCompositeToSkill2,
191674
191800
  assessLocalExecutionResult: () => assessLocalExecutionResult2
191675
191801
  });
191676
191802
  import { existsSync as existsSync50, writeFileSync as writeFileSync26, readFileSync as readFileSync39, mkdirSync as mkdirSync30, readdirSync as readdirSync17 } from "node:fs";
@@ -191847,6 +191973,59 @@ function writeSkillSnapshot2(cacheKey2, skill) {
191847
191973
  return;
191848
191974
  }
191849
191975
  }
191976
+ function compositeSnapshotDir2() {
191977
+ return process.env.UNBROWSE_COMPOSITE_DIR ?? join58(process.env.HOME ?? "/tmp", ".unbrowse", "composites");
191978
+ }
191979
+ function compositeAddress2(domain, target, steps, edges) {
191980
+ const canonical2 = [
191981
+ `domain:${domain}`,
191982
+ `target:${target}`,
191983
+ `steps:${steps.map((s) => s.endpoint_id).join(">")}`,
191984
+ `edges:${edges.map((e) => `${e.from}.${e.binding}->${e.to}`).sort().join("|")}`
191985
+ ].join("::");
191986
+ return `composite:${createHash40("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
191987
+ }
191988
+ function compositeLookupKey2(domain, target) {
191989
+ const canonical2 = `domain:${domain}::target:${target}`;
191990
+ return `lookup:${createHash40("sha256").update(canonical2).digest("hex").slice(0, 32)}`;
191991
+ }
191992
+ function compositeFilePath2(lookupKey2) {
191993
+ return join58(compositeSnapshotDir2(), `${lookupKey2.replace(/[^a-z0-9]/gi, "_")}.json`);
191994
+ }
191995
+ function writeComposite2(c) {
191996
+ if (process.env.UNBROWSE_STATELESS === "1")
191997
+ return;
191998
+ if (process.env.UNBROWSE_LOCAL_CACHES !== "1")
191999
+ return;
192000
+ try {
192001
+ mkdirSync30(compositeSnapshotDir2(), { recursive: true });
192002
+ const target = compositeFilePath2(compositeLookupKey2(c.domain, c.target));
192003
+ writeFileSync26(target, JSON.stringify(c), "utf-8");
192004
+ return target;
192005
+ } catch {
192006
+ return;
192007
+ }
192008
+ }
192009
+ function readComposite2(domain, target) {
192010
+ try {
192011
+ const path23 = compositeFilePath2(compositeLookupKey2(domain, target));
192012
+ if (!existsSync50(path23))
192013
+ return;
192014
+ return JSON.parse(readFileSync39(path23, "utf-8"));
192015
+ } catch {
192016
+ return;
192017
+ }
192018
+ }
192019
+ function findCompositeInSkill2(skill, target) {
192020
+ return skill?.composites?.find((c) => c.target === target && c.steps.length > 0);
192021
+ }
192022
+ function attachCompositeToSkill2(skill, composite) {
192023
+ const list = skill.composites ?? (skill.composites = []);
192024
+ if (list.some((c) => c.composite_id === composite.composite_id))
192025
+ return false;
192026
+ list.push(composite);
192027
+ return true;
192028
+ }
191850
192029
  function hasSearchBindings2(endpoint) {
191851
192030
  const haystack = JSON.stringify({
191852
192031
  url: endpoint.url_template,
@@ -192914,6 +193093,21 @@ function buildCompositeEdges2(target, steps, boundKeys) {
192914
193093
  return from ? { from: from.endpoint_id, binding, to: target } : null;
192915
193094
  }).filter((e) => e !== null);
192916
193095
  }
193096
+ function planPrereqOrder2(livePrereqOrder, persisted, isReplayable) {
193097
+ if (persisted && persisted.steps.length > 0) {
193098
+ const recordedOrder = persisted.steps.map((s) => s.endpoint_id);
193099
+ if (recordedOrder.every((id) => isReplayable(id))) {
193100
+ return {
193101
+ prereqOrder: [
193102
+ ...recordedOrder,
193103
+ ...livePrereqOrder.filter((p) => !recordedOrder.includes(p))
193104
+ ],
193105
+ replayedCompositeId: persisted.composite_id
193106
+ };
193107
+ }
193108
+ }
193109
+ return { prereqOrder: livePrereqOrder };
193110
+ }
192917
193111
  async function walkPrerequisiteChain2(skill, unboundParams, prerequisiteOrder, baseParams, queryIntent, projection, options, steps) {
192918
193112
  const resolved = {};
192919
193113
  const executed = new Map;
@@ -194194,18 +194388,69 @@ async function resolveAndExecute2(intent, params = {}, context, projection, opti
194194
194388
  };
194195
194389
  const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
194196
194390
  let chainBound = {};
194197
- const prereqOrder = dagPlan?.prerequisite_order ?? [];
194391
+ let prereqOrder = dagPlan?.prerequisite_order ?? [];
194392
+ let replayedCompositeId;
194393
+ if (stillUnbound.length > 0) {
194394
+ const replayDomain = skill.domain ?? (() => {
194395
+ try {
194396
+ return new URL(candidate.endpoint.url_template).hostname;
194397
+ } catch {
194398
+ return "";
194399
+ }
194400
+ })();
194401
+ const persisted = findCompositeInSkill2(skill, candidate.endpoint.endpoint_id) ?? readComposite2(replayDomain, candidate.endpoint.endpoint_id);
194402
+ const decision = planPrereqOrder2(prereqOrder, persisted, (id) => {
194403
+ const ep = skill.endpoints.find((e) => e.endpoint_id === id);
194404
+ return ep != null && canAutoExecuteEndpoint(ep);
194405
+ });
194406
+ prereqOrder = decision.prereqOrder;
194407
+ replayedCompositeId = decision.replayedCompositeId;
194408
+ if (replayedCompositeId) {
194409
+ console.log(`[chain] composite replay → ${candidate.endpoint.endpoint_id} (${replayedCompositeId})`);
194410
+ }
194411
+ }
194198
194412
  if (stillUnbound.length > 0 && prereqOrder.length > 0) {
194199
194413
  const chainSteps = [];
194200
194414
  chainBound = await walkPrerequisiteChain2(skill, stillUnbound, prereqOrder, { ...templateDefaults, ...endpointParams, ...syncInferred, ...searchOverrides }, queryIntent, projection, { ...options, intent: queryIntent, contextUrl: context?.url }, chainSteps);
194201
194415
  if (chainSteps.length > 0) {
194416
+ const compositeEdges = buildCompositeEdges2(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound));
194417
+ const compositeDomain = skill.domain ?? (() => {
194418
+ try {
194419
+ return new URL(candidate.endpoint.url_template).hostname;
194420
+ } catch {
194421
+ return "";
194422
+ }
194423
+ })();
194424
+ const compositeIntentSig = (queryIntent ?? "").trim().toLowerCase();
194425
+ const compositeId = compositeAddress2(compositeDomain, candidate.endpoint.endpoint_id, chainSteps, compositeEdges);
194202
194426
  const composite = {
194427
+ composite_id: compositeId,
194203
194428
  target: candidate.endpoint.endpoint_id,
194204
194429
  steps: chainSteps,
194205
- edges: buildCompositeEdges2(candidate.endpoint.endpoint_id, chainSteps, Object.keys(chainBound))
194430
+ edges: compositeEdges,
194431
+ mode: replayedCompositeId === compositeId ? "composite_replay" : "composite_walk"
194206
194432
  };
194207
194433
  decisionTrace.prerequisite_chain = chainSteps;
194208
194434
  decisionTrace.composite = composite;
194435
+ if (chainSteps.every((s) => s.ok) && canAutoExecuteEndpoint(candidate.endpoint)) {
194436
+ const descriptor = {
194437
+ composite_id: compositeId,
194438
+ intent_signature: compositeIntentSig,
194439
+ domain: compositeDomain,
194440
+ target: composite.target,
194441
+ steps: chainSteps,
194442
+ edges: compositeEdges,
194443
+ created_at: new Date().toISOString()
194444
+ };
194445
+ const composedNewlyAttached = attachCompositeToSkill2(skill, descriptor);
194446
+ if (composedNewlyAttached && skill.skill_id) {
194447
+ queuePassiveSkillPublish(skill).catch(() => {});
194448
+ }
194449
+ const persistedPath = writeComposite2(descriptor);
194450
+ if (persistedPath) {
194451
+ decisionTrace.composite_persisted = compositeId;
194452
+ }
194453
+ }
194209
194454
  console.log(`[chain] composite → ${composite.target} via ` + composite.edges.map((e) => `${e.from}.${e.binding}`).join(" + ") || "(no edges)");
194210
194455
  }
194211
194456
  }
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-14T02:28:58.704Z",
5
+ "built_at": "2026-06-14T04:52:03.906Z",
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": "258ae9db1a86d21d39e248904135693f3e20ce2fa4b32e3be48c4b32ebc9989e"
24
+ "sha256": "473995d9a14513c1dfd3a86d29f0c10fb6f4b735565f9879574a325a4efbe617"
25
25
  },
26
26
  "win-x64": {
27
27
  "zig_target": "x86_64-windows-gnu",
28
- "sha256": "d1a16044a5425e8e2b038af4c8d052021857829d8f08e30eccfbbe5ca62989c1"
28
+ "sha256": "5c98de92a1675ec1e1d823b2b0bd9c6ab90c4b32a8c34cdcf51c84e240a377c9"
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": "b059e6b94010942376c7c6de683b84b937477413a8a77b826700f235826b5634"
35
+ "sha256": "ebf5ef3f77623b67799702029de32b5ade1dc8b758c379740998d20d2bc9d10c"
36
36
  },
37
37
  "darwin-x64": {
38
38
  "zig_target": "x86_64-macos",
39
39
  "lib": "libkuri_ffi.dylib",
40
- "sha256": "de9856b3350bc26ecd8e4c167b46361bad9e5f775b61a637970bce38e596da1d"
40
+ "sha256": "7dfdb81bab83b861245afefea9bab1a7342f646ced84e62ecdd4d9f1d6ac589c"
41
41
  },
42
42
  "linux-arm64": {
43
43
  "zig_target": "aarch64-linux",
44
44
  "lib": "libkuri_ffi.so",
45
- "sha256": "e088ff534085f03cd4f79629bd7ffc2a4731aaabb4f28200edb1d55d873a29ad"
45
+ "sha256": "435454b86a17ca8605c7129b50f7f090ead9c4928e5337b1b833fda7f4e82403"
46
46
  },
47
47
  "linux-x64": {
48
48
  "zig_target": "x86_64-linux",
49
49
  "lib": "libkuri_ffi.so",
50
- "sha256": "1250b2a48a181a15abf7019cd66d6e136ff13c89dd56e205bd5b0c740ed06f16"
50
+ "sha256": "3d05e8b0203d24c6d1b051381e57e883f7fcc0bda0a86d75c2b4d243fb15212b"
51
51
  }
52
52
  }
53
53
  }
Binary file