@danceiny/gotry 0.0.1-rc.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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/bin/gotry-inner.js +214 -0
  4. package/bin/gotry-stdio-ask.js +59 -0
  5. package/bin/gotry.js +28 -0
  6. package/cordis.gotry-patch.yml +86 -0
  7. package/data/flights_2026.json +218 -0
  8. package/data/golden_erhai.json +92 -0
  9. package/data/golden_trip_2026.json +75 -0
  10. package/data/hotels_2026.json +151 -0
  11. package/data/openflights-skeleton.json +895 -0
  12. package/data/time-slot-eval.json +384 -0
  13. package/data/yunnan-pack.json +202 -0
  14. package/dist/capabilities/agent-reach-bridge.py +90 -0
  15. package/dist/capabilities/agent-reach-deep.js +172 -0
  16. package/dist/capabilities/agent-reach.js +204 -0
  17. package/dist/capabilities/anything.js +139 -0
  18. package/dist/capabilities/flyai.js +131 -0
  19. package/dist/capabilities/hbcli.js +143 -0
  20. package/dist/capabilities/incident-log.js +100 -0
  21. package/dist/capabilities/opensky.js +74 -0
  22. package/dist/capabilities/session/action-cache.js +120 -0
  23. package/dist/capabilities/session/adapters/ctrip-flight.js +83 -0
  24. package/dist/capabilities/session/adapters/meituan-local.js +73 -0
  25. package/dist/capabilities/session/extract.js +40 -0
  26. package/dist/capabilities/session/read-guard.js +99 -0
  27. package/dist/capabilities/session/transport.js +101 -0
  28. package/dist/capabilities/session-search.js +93 -0
  29. package/dist/capabilities/weather.js +180 -0
  30. package/dist/scripts/action-cache-tests.js +107 -0
  31. package/dist/scripts/agent-reach-deep-tests.js +54 -0
  32. package/dist/scripts/agent-reach-tests.js +38 -0
  33. package/dist/scripts/agent-reach-wrapper-tests.js +79 -0
  34. package/dist/scripts/anything-tests.js +95 -0
  35. package/dist/scripts/async-collect.js +37 -0
  36. package/dist/scripts/companion-tests.js +112 -0
  37. package/dist/scripts/diff-test.js +39 -0
  38. package/dist/scripts/engine-run.js +14 -0
  39. package/dist/scripts/engine-tests.js +45 -0
  40. package/dist/scripts/hbcli-tests.js +101 -0
  41. package/dist/scripts/incident-tests.js +96 -0
  42. package/dist/scripts/journey-tests.js +45 -0
  43. package/dist/scripts/ledger-tests.js +365 -0
  44. package/dist/scripts/ledger-workflow-crash.js +42 -0
  45. package/dist/scripts/memory-capture-tests.js +77 -0
  46. package/dist/scripts/memory-decay-tests.js +83 -0
  47. package/dist/scripts/memory-metrics.js +24 -0
  48. package/dist/scripts/nudge-digest.js +83 -0
  49. package/dist/scripts/opensky-check.js +20 -0
  50. package/dist/scripts/opensky-tests.js +52 -0
  51. package/dist/scripts/probe-poi-tests.js +88 -0
  52. package/dist/scripts/publish-preverify.js +68 -0
  53. package/dist/scripts/replay-async.js +44 -0
  54. package/dist/scripts/replay-real.js +38 -0
  55. package/dist/scripts/replay.js +116 -0
  56. package/dist/scripts/session-attach-diagnose.js +27 -0
  57. package/dist/scripts/session-attach-poc.js +65 -0
  58. package/dist/scripts/session-extract-tests.js +81 -0
  59. package/dist/scripts/session-tests.js +162 -0
  60. package/dist/scripts/skeleton-check.js +37 -0
  61. package/dist/scripts/skeleton-integration-test.js +22 -0
  62. package/dist/scripts/skills-contract-tests.js +85 -0
  63. package/dist/scripts/smoke.js +328 -0
  64. package/dist/scripts/state-cli-tests.js +136 -0
  65. package/dist/scripts/state-cli.js +248 -0
  66. package/dist/scripts/time-eval-tests.js +318 -0
  67. package/dist/scripts/travel-timeline-tests.js +123 -0
  68. package/dist/scripts/unified-tests.js +49 -0
  69. package/dist/scripts/weather-tests.js +48 -0
  70. package/dist/src/bridge.js +34 -0
  71. package/dist/src/companions.js +112 -0
  72. package/dist/src/contracts.js +64 -0
  73. package/dist/src/dsh-llm.js +174 -0
  74. package/dist/src/engine.js +331 -0
  75. package/dist/src/index.js +1239 -0
  76. package/dist/src/journey.js +147 -0
  77. package/dist/src/loop.js +360 -0
  78. package/dist/src/memory-capture.js +40 -0
  79. package/dist/src/memory-decay.js +31 -0
  80. package/dist/src/memory-utility.js +55 -0
  81. package/dist/src/mock-llm.js +103 -0
  82. package/dist/src/model.js +134 -0
  83. package/dist/src/slot-spec.js +165 -0
  84. package/dist/src/state-ledger.js +901 -0
  85. package/dist/src/time-anchor.js +100 -0
  86. package/dist/src/tool-packet.js +12 -0
  87. package/dist/src/travel-slots.js +144 -0
  88. package/dist/src/travel-timeline.js +78 -0
  89. package/dist/src/unified.js +538 -0
  90. package/dist/src/wish-pool.js +27 -0
  91. package/package.json +84 -0
  92. package/ts/capabilities/agent-reach-deep.ts +114 -0
  93. package/ts/capabilities/agent-reach.ts +158 -0
  94. package/ts/capabilities/anything.ts +201 -0
  95. package/ts/capabilities/flyai.ts +151 -0
  96. package/ts/capabilities/hbcli.ts +152 -0
  97. package/ts/capabilities/incident-log.ts +148 -0
  98. package/ts/capabilities/opensky.ts +121 -0
  99. package/ts/capabilities/session/action-cache.ts +148 -0
  100. package/ts/capabilities/session/adapters/ctrip-flight.ts +107 -0
  101. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  102. package/ts/capabilities/session/extract.ts +54 -0
  103. package/ts/capabilities/session/read-guard.ts +141 -0
  104. package/ts/capabilities/session/transport.ts +114 -0
  105. package/ts/capabilities/session-search.ts +129 -0
  106. package/ts/capabilities/weather.ts +176 -0
  107. package/ts/package.json +27 -0
  108. package/ts/scripts/async-collect.ts +50 -0
  109. package/ts/scripts/skeleton-check.ts +49 -0
  110. package/ts/scripts/skeleton-integration-test.ts +23 -0
  111. package/ts/scripts/state-cli.ts +201 -0
  112. package/ts/src/bridge.ts +44 -0
  113. package/ts/src/companions.ts +113 -0
  114. package/ts/src/contracts.ts +165 -0
  115. package/ts/src/dsh-llm.ts +155 -0
  116. package/ts/src/index.ts +968 -0
  117. package/ts/src/loop.ts +411 -0
  118. package/ts/src/memory-capture.ts +59 -0
  119. package/ts/src/memory-decay.ts +55 -0
  120. package/ts/src/memory-utility.ts +70 -0
  121. package/ts/src/mock-llm.ts +100 -0
  122. package/ts/src/model.ts +220 -0
  123. package/ts/src/slot-spec.ts +172 -0
  124. package/ts/src/state-ledger.ts +849 -0
  125. package/ts/src/time-anchor.ts +134 -0
  126. package/ts/src/tool-packet.ts +32 -0
  127. package/ts/src/travel-slots.ts +219 -0
  128. package/ts/src/travel-timeline.ts +83 -0
  129. package/ts/src/unified.ts +582 -0
  130. package/ts/src/wish-pool.ts +66 -0
@@ -0,0 +1,538 @@
1
+ import { checkConnectivity } from '../scripts/skeleton-check.js';
2
+ import { evaluateChoice, minToHhmm, hhmmToMin, requiredUsableHours, trueCostToDict, LATEST_ARRIVE_STAY_MIN } from './model.js';
3
+ let z3Promise = null;
4
+ async function getZ3() {
5
+ if (!z3Promise) {
6
+ const { init } = await import('z3-solver');
7
+ z3Promise = (async ()=>(await init()).Context('main'))();
8
+ }
9
+ return z3Promise;
10
+ }
11
+ const SEGMENT_ROUTES = {
12
+ f1: 'HKG->HKT',
13
+ f2: 'HKT->BKK',
14
+ f3: 'BKK->KMG',
15
+ f4: 'KMG->SZX',
16
+ f5: 'SZX->DXB',
17
+ yn1: 'SZX->LJG',
18
+ yn2: 'LJG->DLU',
19
+ yn3: 'DLU->LJG',
20
+ yn4: 'LJG->SZX'
21
+ };
22
+ function routeHint(id) {
23
+ return SEGMENT_ROUTES[id];
24
+ }
25
+ export function segmentsFromCandidate(req, candidates) {
26
+ const motHard = req.motivation;
27
+ const wakeFloor = motHard.wakeFloorMin;
28
+ const options = candidates.map((c)=>({
29
+ id: c.id,
30
+ label: c.name,
31
+ score: c.imageryMatch,
32
+ bestMonths: c.bestMonths,
33
+ minDays: c.minDaysForPurpose,
34
+ move: {
35
+ hub: c.hub,
36
+ services: [
37
+ ...c.servicesOut
38
+ ],
39
+ retServices: [
40
+ ...c.servicesRet
41
+ ],
42
+ transfers: [
43
+ ...c.destTransfers
44
+ ],
45
+ bufferMin: c.bufferOutMin,
46
+ bufferRetMin: c.bufferRetMin,
47
+ originTransferMin: 0,
48
+ destTransferMin: 0
49
+ },
50
+ stay: {
51
+ nights: req.windowDays - 1,
52
+ stayCnyPerNight: c.stayCnyPerNight,
53
+ localDailyCny: c.localDailyCny
54
+ }
55
+ }));
56
+ const escape = req.motivation.weights['escape_rest'] ?? 0;
57
+ return {
58
+ note: req.note,
59
+ segments: [
60
+ {
61
+ id: 'dest',
62
+ role: 'choice',
63
+ note: '目的地选择',
64
+ anchors: {
65
+ wakeFloorMin: wakeFloor
66
+ },
67
+ options
68
+ }
69
+ ],
70
+ budgetCny: req.budgetCny,
71
+ defaultWakeFloorMin: wakeFloor
72
+ };
73
+ }
74
+ export function parseFlightPackToSpec(pack) {
75
+ const legs = pack['legs'].map((l)=>({
76
+ id: String(l['id']),
77
+ role: l['services'].length === 1 ? 'fixed' : 'choice',
78
+ note: l['note'] ? String(l['note']) : undefined,
79
+ date: l['date'] ? String(l['date']) : undefined,
80
+ anchors: {
81
+ arriveByMin: l['arrive_by'] ? hhmmToMin(String(l['arrive_by'])) : undefined
82
+ },
83
+ route: routeHint(l['id']),
84
+ options: l['services'].map((sv)=>({
85
+ id: String(sv['id']),
86
+ label: `${String(sv['id'])} ${String(l['note'] ?? '').slice(0, 18)}`,
87
+ move: {
88
+ hub: String(l['hub'] ?? ''),
89
+ services: [
90
+ {
91
+ id: String(sv['id']),
92
+ depMin: hhmmToMin(String(sv['dep'])),
93
+ arrMin: hhmmToMin(String(sv['arr'])),
94
+ priceCny: Number(sv['price_cny'])
95
+ }
96
+ ],
97
+ bufferMin: Number(l['buffer_min']),
98
+ originTransferMin: Number(l['origin_transfer_min']),
99
+ destTransferMin: Number(l['dest_transfer_min']),
100
+ redEye: Boolean(l['red_eye']),
101
+ redEyeDurationMin: Number(l['red_eye_duration_min'] ?? 0),
102
+ tzOffsetMin: Number(l['tz_offset_min'] ?? 0),
103
+ originTzOffsetMin: Number(l['origin_tz_offset_min'] ?? 480)
104
+ }
105
+ }))
106
+ }));
107
+ const meta = pack['meta'] ?? {};
108
+ const ww = meta['work_window'];
109
+ const spec = {
110
+ segments: legs,
111
+ workWindow: ww ? {
112
+ homeTzOffsetMin: Number(ww['home_tz_offset_min']),
113
+ startMin: Number(ww['start_min']),
114
+ endMin: Number(ww['end_min']),
115
+ workdays: ww['workdays'] ?? [
116
+ 0,
117
+ 1,
118
+ 2,
119
+ 3,
120
+ 4
121
+ ]
122
+ } : undefined
123
+ };
124
+ for (const l of pack['legs']){
125
+ const seg = spec.segments.find((s)=>s.id === l['id']);
126
+ for (const svc of l['services']){
127
+ if (svc['weekday']) {
128
+ const opt = seg.options.find((o)=>o.id === svc['id']);
129
+ if (opt) opt.depWeekday = String(svc['weekday']);
130
+ }
131
+ }
132
+ }
133
+ return spec;
134
+ }
135
+ const WEEKDAY_IDX = {
136
+ mon: 0,
137
+ tue: 1,
138
+ wed: 2,
139
+ thu: 3,
140
+ fri: 4,
141
+ sat: 5,
142
+ sun: 6
143
+ };
144
+ const WEEKDAY_CN = {
145
+ mon: '一',
146
+ tue: '二',
147
+ wed: '三',
148
+ thu: '四',
149
+ fri: '五',
150
+ sat: '六',
151
+ sun: '日'
152
+ };
153
+ function workWindowBlocks(spec, option) {
154
+ const ww = spec.workWindow;
155
+ if (!ww || !option.move || !option.depWeekday) return null;
156
+ if (!(ww.workdays ?? [
157
+ 0,
158
+ 1,
159
+ 2,
160
+ 3,
161
+ 4
162
+ ]).includes(WEEKDAY_IDX[option.depWeekday])) return null;
163
+ const dep = option.move.services[0].depMin;
164
+ const shift = (option.move.originTzOffsetMin ?? 480) - ww.homeTzOffsetMin;
165
+ const start = ww.startMin + shift, end = ww.endMin + shift;
166
+ if (start <= dep && dep <= end) {
167
+ const hhmm = (m)=>`${String(Math.floor(m / 60)).padStart(2, '0')}:${String(m % 60).padStart(2, '0')}`;
168
+ return `周${WEEKDAY_CN[option.depWeekday]} ${hhmm(dep)} 起飞落在工作窗口(当地 ${hhmm(start)}-${hhmm(end)})内`;
169
+ }
170
+ return null;
171
+ }
172
+ function evaluateOptionMove(segId, mv) {
173
+ const svc = mv.services[0];
174
+ const wake = svc.depMin - mv.bufferMin - mv.originTransferMin;
175
+ const trueFlight = svc.arrMin - svc.depMin - (mv.tzOffsetMin ?? 0);
176
+ const d2d = mv.bufferMin + mv.originTransferMin + trueFlight + mv.destTransferMin;
177
+ const wakeDisplay = wake < 0 ? `${minToHhmm(wake + 1440)}(前一日)` : minToHhmm(wake);
178
+ const arriveStay = svc.arrMin + mv.destTransferMin;
179
+ let energy;
180
+ if (mv.redEye && (mv.redEyeDurationMin ?? 0) > 0) {
181
+ const sleepH = ((mv.redEyeDurationMin ?? 0) - 60) / 60;
182
+ const groundH = (mv.groundRecoveryMin ?? mv.destTransferMin ?? 0) / 60;
183
+ const recovery = Math.min(5, 5 * groundH);
184
+ energy = Math.max(30, Math.min(75 + recovery, 80, 30 + 8 * sleepH + recovery));
185
+ } else {
186
+ energy = 100 - 2 * 8;
187
+ if (wake < 5 * 60) energy -= 30;
188
+ else if (wake < 6 * 60) energy -= 25;
189
+ if (arriveStay > 21 * 60) energy -= 10;
190
+ if (d2d > 6 * 60) energy -= 10;
191
+ energy = Math.max(0, energy);
192
+ }
193
+ return {
194
+ service: svc.id,
195
+ dep: minToHhmm(svc.depMin),
196
+ wake: wakeDisplay,
197
+ wakeMin: wake,
198
+ arrive_stay: minToHhmm(arriveStay),
199
+ door_to_door: `${Math.floor(d2d / 60)}h${String(d2d % 60).padStart(2, '0')}m`,
200
+ d2d_min: d2d,
201
+ energy_pct: Math.round(energy),
202
+ price_cny: svc.priceCny
203
+ };
204
+ }
205
+ export async function solveUnified(spec) {
206
+ try {
207
+ return await solveUnifiedInner(spec);
208
+ } catch (e) {
209
+ console.error('[gotry] solveUnified failed (likely wasm thread race):', e.message?.slice(0, 200));
210
+ return {
211
+ feasible: false,
212
+ unsat_core: [
213
+ 'wasm_runtime_error'
214
+ ],
215
+ red_flags: [
216
+ 'WASM 求解器异常,建议下次用候选形态(枚举)重试'
217
+ ]
218
+ };
219
+ }
220
+ }
221
+ async function solveUnifiedInner(spec) {
222
+ const skeletonNotes = [];
223
+ const exclusions = [];
224
+ for (const seg of spec.segments){
225
+ if (spec.skeletonHub) {
226
+ const route = seg.route;
227
+ if (route) {
228
+ const [a, b] = route.split('->').map((s)=>s.trim());
229
+ const verdict = await checkConnectivity(a, b);
230
+ skeletonNotes.push(`${seg.id}: ${verdict.evidence}`);
231
+ }
232
+ }
233
+ const kept = seg.options.filter((o)=>{
234
+ const reason = workWindowBlocks(spec, o);
235
+ if (reason) exclusions.push({
236
+ segment: seg.id,
237
+ option: o.id,
238
+ reason
239
+ });
240
+ return !reason;
241
+ });
242
+ seg.options = kept;
243
+ if (kept.length === 0) {
244
+ return {
245
+ feasible: false,
246
+ unsat_core: [
247
+ `${seg.id}:work_window`
248
+ ],
249
+ work_window_exclusions: exclusions
250
+ };
251
+ }
252
+ }
253
+ const z3 = await getZ3();
254
+ const { Bool, If, Int, Solver, Sum } = z3;
255
+ const wakeFloor = spec.defaultWakeFloorMin ?? hhmmToMin('06:00');
256
+ const allSels = {};
257
+ const assertions = {};
258
+ const svcOf = {};
259
+ for (const seg of spec.segments){
260
+ const sels = seg.options.map((_, i)=>Bool.const(`${seg.id}_o${i}`));
261
+ allSels[seg.id] = sels;
262
+ const svcs = seg.options.map((o)=>o.move.services[0]);
263
+ svcOf[seg.id] = svcs;
264
+ const pick = (attr)=>Sum(...sels.map((s, i)=>If(s, Int.val(svcs[i][attr]), Int.val(0))));
265
+ const dep = pick('depMin'), arr = pick('arrMin');
266
+ const o0 = seg.options[0].move;
267
+ const wake = dep.sub(Int.val(o0.bufferMin + o0.originTransferMin));
268
+ const arriveStay = arr.add(Int.val(o0.destTransferMin));
269
+ if (seg.anchors?.arriveByMin !== undefined) assertions[`${seg.id}:arrive_by`] = arriveStay.le(Int.val(seg.anchors.arriveByMin));
270
+ if (seg.anchors?.departAfterMin !== undefined) assertions[`${seg.id}:depart_after`] = dep.ge(Int.val(seg.anchors.departAfterMin));
271
+ if (!seg.options.some((o)=>o.move?.redEye)) assertions[`${seg.id}:wake_floor`] = wake.ge(Int.val(wakeFloor));
272
+ }
273
+ const total = Sum(...spec.segments.flatMap((seg)=>allSels[seg.id].map((s, i)=>If(s, Int.val(svcOf[seg.id][i].priceCny), Int.val(0)))));
274
+ if (spec.budgetCny !== undefined) assertions['total:budget'] = total.le(Int.val(spec.budgetCny));
275
+ const exactlyOne = (sels)=>Sum(...sels.map((s)=>If(s, Int.val(1), Int.val(0)))).eq(Int.val(1));
276
+ const coreOf = (s)=>{
277
+ const v = s.unsatCore();
278
+ const out = [];
279
+ for(let i = 0; i < v.length(); i++)out.push(String(v.get(i)).replace(/^\|/, '').replace(/\|$/, ''));
280
+ return out;
281
+ };
282
+ const s = new Solver();
283
+ for (const sel of Object.values(allSels))s.add(exactlyOne(sel));
284
+ for (const [name, expr] of Object.entries(assertions))s.addAndTrack(expr, name);
285
+ const report = async (model)=>{
286
+ const out = [];
287
+ for (const seg of spec.segments){
288
+ for(let i = 0; i < allSels[seg.id].length; i++){
289
+ const v = await maybeAwait(model.eval(allSels[seg.id][i], true));
290
+ if (String(v) !== 'true') continue;
291
+ const o = seg.options[i];
292
+ out.push({
293
+ leg: seg.id,
294
+ ...evaluateOptionMove(seg.id, o.move)
295
+ });
296
+ break;
297
+ }
298
+ }
299
+ return out;
300
+ };
301
+ async function maybeAwait(v) {
302
+ return v instanceof Promise ? await v : v;
303
+ }
304
+ if (String(await s.check()) !== 'unsat') {
305
+ const legs = await report(s.model());
306
+ const money = legs.reduce((a, l)=>a + l.price_cny, 0);
307
+ const redFlags = legs.filter((l)=>spec.segments.find((sg)=>sg.id === l.leg)?.options.some((o)=>o.move?.redEye) && l.energy_pct < 50).map((l)=>`${l.leg} 落地精力仅 ${l.energy_pct}%(红眼后直奔事务,当日不宜安排重要会议)`);
308
+ return {
309
+ feasible: true,
310
+ money_cny: money,
311
+ legs,
312
+ red_flags: redFlags,
313
+ work_window_exclusions: exclusions,
314
+ skeleton_notes: skeletonNotes.length ? skeletonNotes : undefined
315
+ };
316
+ }
317
+ const core = coreOf(s).sort();
318
+ const suggestions = [];
319
+ for (const name of core){
320
+ const s2 = new Solver();
321
+ for (const sel of Object.values(allSels))s2.add(exactlyOne(sel));
322
+ for (const [n2, expr] of Object.entries(assertions)){
323
+ if (n2 !== name) s2.addAndTrack(expr, n2);
324
+ }
325
+ if (String(await s2.check()) !== 'unsat') {
326
+ const legs = await report(s2.model());
327
+ suggestions.push({
328
+ relax: name,
329
+ money_cny: legs.reduce((a, l)=>a + l.price_cny, 0)
330
+ });
331
+ }
332
+ }
333
+ return {
334
+ feasible: false,
335
+ unsat_core: core,
336
+ suggestions
337
+ };
338
+ }
339
+ function checkTrueCost(t, spec) {
340
+ const fails = [];
341
+ if (t.wakeMin < spec.wakeFloorMin) fails.push('wake_floor');
342
+ if (t.energyArrivalPct < spec.minArrivalEnergyPct) fails.push('energy_floor');
343
+ if (t.usableHours < spec.requiredUsableHours) fails.push('usable_hours');
344
+ if (spec.budgetCny !== undefined && t.moneyCny > spec.budgetCny) fails.push('budget');
345
+ if (t.arriveStayMin > spec.latestArriveStayMin) fails.push('arrival_before_evening');
346
+ return fails;
347
+ }
348
+ function enumerateCombos(opt, cand, req, days) {
349
+ const mv = opt.move;
350
+ const out = [];
351
+ const spec = {
352
+ wakeFloorMin: req.motivation.wakeFloorMin,
353
+ minArrivalEnergyPct: req.motivation.minArrivalEnergyPct,
354
+ requiredUsableHours: requiredUsableHours(req.motivation),
355
+ budgetCny: req.budgetCny,
356
+ latestArriveStayMin: LATEST_ARRIVE_STAY_MIN
357
+ };
358
+ for (const o of mv.services){
359
+ for (const tr of mv.transfers ?? []){
360
+ for (const r of mv.retServices ?? mv.services){
361
+ for (const trr of mv.transfers ?? []){
362
+ const ch = {
363
+ outService: o,
364
+ outTransfer: tr,
365
+ retService: r,
366
+ retTransfer: trr,
367
+ days
368
+ };
369
+ const t = evaluateChoice(cand, req, ch);
370
+ out.push({
371
+ choice: ch,
372
+ cost: t,
373
+ fails: checkTrueCost(t, spec)
374
+ });
375
+ }
376
+ }
377
+ }
378
+ }
379
+ return out;
380
+ }
381
+ function optionToCandidate(opt) {
382
+ const mv = opt.move, st = opt.stay;
383
+ return {
384
+ id: opt.id,
385
+ name: opt.label,
386
+ hub: mv.hub,
387
+ bufferOutMin: mv.bufferMin,
388
+ bufferRetMin: mv.bufferRetMin ?? mv.bufferMin,
389
+ servicesOut: [
390
+ ...mv.services
391
+ ],
392
+ servicesRet: [
393
+ ...mv.retServices ?? mv.services
394
+ ],
395
+ destTransfers: [
396
+ ...mv.transfers ?? []
397
+ ],
398
+ stayCnyPerNight: st.stayCnyPerNight ?? 0,
399
+ localDailyCny: st.localDailyCny ?? 0,
400
+ minDaysForPurpose: opt.minDays ?? 1,
401
+ imageryMatch: opt.score ?? 0,
402
+ bestMonths: opt.bestMonths ?? []
403
+ };
404
+ }
405
+ export function solveChoiceSegment(spec, req) {
406
+ const seg = spec.segments.find((s)=>s.role === 'choice' && s.options.length > 0 && s.options[0].stay);
407
+ if (!seg) throw new Error('solveChoiceSegment: no choice segment with stay found');
408
+ const windowDays = req.windowDays;
409
+ const verdicts = [];
410
+ for (const opt of seg.options){
411
+ const cand = optionToCandidate(opt);
412
+ const minDays = cand.minDaysForPurpose;
413
+ const durationOk = windowDays >= minDays;
414
+ const combos = durationOk ? enumerateCombos(opt, cand, req, windowDays) : [];
415
+ const good = combos.filter((c)=>c.fails.length === 0);
416
+ if (good.length > 0) {
417
+ const best = good.reduce((a, b)=>a.cost.moneyCny <= b.cost.moneyCny ? a : b);
418
+ verdicts.push({
419
+ candidate_id: opt.id,
420
+ name: opt.label,
421
+ feasible: true,
422
+ imagery_match: opt.score ?? 0,
423
+ chosen: {
424
+ out_service: best.choice.outService.id,
425
+ out_transfer: best.choice.outTransfer.mode,
426
+ ret_service: best.choice.retService.id,
427
+ ret_transfer: best.choice.retTransfer.mode,
428
+ days: best.choice.days
429
+ },
430
+ true_cost: trueCostToDict(best.cost)
431
+ });
432
+ continue;
433
+ }
434
+ const blocked = durationOk ? Array.from(new Set(combos.flatMap((c)=>c.fails))).sort() : [
435
+ 'duration'
436
+ ];
437
+ const suggestions = [];
438
+ for (const name of blocked){
439
+ const opened = combos.filter((c)=>!c.fails.includes(name));
440
+ if (opened.length > 0) {
441
+ const cheapest = opened.reduce((a, b)=>a.cost.moneyCny <= b.cost.moneyCny ? a : b);
442
+ suggestions.push({
443
+ relax: name,
444
+ resulting_money_cny: cheapest.cost.moneyCny
445
+ });
446
+ }
447
+ }
448
+ let wish = null;
449
+ if (!durationOk) {
450
+ const longCombos = enumerateCombos(opt, cand, req, minDays);
451
+ const budgetDropped = longCombos.filter((c)=>c.fails.length === 0 || c.fails.length === 1 && c.fails[0] === 'budget');
452
+ const conds = {
453
+ days: minDays
454
+ };
455
+ if (budgetDropped.length > 0) {
456
+ conds['budget_cny'] = Math.min(...budgetDropped.map((c)=>c.cost.moneyCny));
457
+ }
458
+ if (opt.bestMonths?.length) conds['best_months'] = opt.bestMonths;
459
+ wish = {
460
+ name: opt.label,
461
+ conditions: conds,
462
+ reason: `${windowDays} 天窗口装不下(目的需 ${minDays} 天)`
463
+ };
464
+ }
465
+ verdicts.push({
466
+ candidate_id: opt.id,
467
+ name: opt.label,
468
+ feasible: false,
469
+ imagery_match: opt.score ?? 0,
470
+ unsat_core: blocked,
471
+ suggestions,
472
+ wish_pool: wish
473
+ });
474
+ }
475
+ const feasible = verdicts.filter((v)=>v.feasible).sort((a, b)=>b.imagery_match - a.imagery_match);
476
+ return {
477
+ verdicts,
478
+ recommended: feasible.length > 0 ? feasible[0]['candidate_id'] : null,
479
+ answer_md: renderCandidateMarkdown(req, verdicts, feasible.length > 0 ? String(feasible[0]['candidate_id']) : null)
480
+ };
481
+ }
482
+ function renderCandidateMarkdown(req, verdicts, recommended) {
483
+ const feasible = verdicts.filter((v)=>v['feasible']).sort((a, b)=>b['imagery_match'] - a['imagery_match']);
484
+ const parked = verdicts.filter((v)=>!v['feasible']);
485
+ const lines = [];
486
+ lines.push(`> 憧憬:${req.note}`);
487
+ lines.push(`> 已识别约束:窗口 ${req.windowDays} 天 | 预算 ¥${req.budgetCny} | ` + `动机(休整改写需求 ${requiredUsableHours(req.motivation).toFixed(1)}h 有效休整)`);
488
+ lines.push('');
489
+ for (const v of parked){
490
+ const core = v['unsat_core'] ?? [];
491
+ lines.push(`**${v['name']}:现在不行**——冲突约束:${core.join('、')}。`);
492
+ const sug = (v['suggestions'] ?? [])[0];
493
+ if (sug) lines.push(`- 放宽 ${sug['relax']}:约 ¥${sug['resulting_money_cny']}`);
494
+ const wish = v['wish_pool'];
495
+ if (wish) {
496
+ const conds = wish['conditions'];
497
+ const budgetNote = conds['budget_cny'] ? `、约 ¥${conds['budget_cny']}` : '';
498
+ const months = conds['best_months'];
499
+ const season = months ? `,${months} 月最佳` : '';
500
+ lines.push(`- 已放入「下一次出发」清单:需要 ${conds['days']} 天${budgetNote}${season}`);
501
+ }
502
+ }
503
+ lines.push('');
504
+ for (const v of feasible){
505
+ const ch = v['chosen'];
506
+ const t = v['true_cost'];
507
+ lines.push(`**${v['name']}:可行**` + `(${ch['out_service']} 出发,${ch['out_transfer']} 接驳,` + `起床 ${t['wake']},到达精力 ${t['energy_arrival_pct']}%,` + `门到门 ${t['door_to_door_out']},有效休整 ${t['usable_hours']}h,共 ¥${t['money_cny']})`);
508
+ }
509
+ if (feasible.length) {
510
+ lines.push('');
511
+ const best = feasible[0];
512
+ const alt = feasible[1];
513
+ lines.push(`**建议:${best['name']}**(意象匹配 ${(best['imagery_match'] * 100).toFixed(0)}%)。` + (alt ? `备选:${alt['name']}(¥${alt['true_cost']['money_cny']},匹配 ${(alt['imagery_match'] * 100).toFixed(0)}%)。` : ''));
514
+ }
515
+ lines.push('');
516
+ lines.push('**待你决定的两个问题**:');
517
+ if (feasible.length >= 2) {
518
+ lines.push(`1. ${feasible[0]['name']} 还是 ${feasible[1]['name']}?(前者更贴意象,后者更省)`);
519
+ } else if (feasible.length === 1) {
520
+ lines.push(`1. 就去 ${feasible[0]['name']} 吗?`);
521
+ } else {
522
+ lines.push('1. 所有候选都不可行——考虑放宽哪条约束?');
523
+ }
524
+ const p0 = parked[0];
525
+ const p0wish = p0?.['wish_pool'];
526
+ if (p0wish) {
527
+ const conds = p0wish['conditions'];
528
+ lines.push(`2. 把 ${p0['name']} 留给「下一次出发」(${conds['days']} 天起),这次先去可行的?`);
529
+ } else if (feasible.length) {
530
+ const ch = feasible[0]['chosen'];
531
+ lines.push(`2. 出发班次选 ${ch['out_service']} 还是更晚的?`);
532
+ }
533
+ return lines.join('\n');
534
+ }
535
+ export { minToHhmm };
536
+
537
+
538
+ //# sourceURL=/Users/bytedance/work/gotry/ts/src/unified.ts
@@ -0,0 +1,27 @@
1
+ export function scoreWishMatch(entry, ctx) {
2
+ const c = entry.conditions ?? {};
3
+ const hits = [];
4
+ if (typeof ctx.days === 'number' && typeof c.days === 'number' && ctx.days >= c.days) hits.push(`days≥${c.days}`);
5
+ if (typeof ctx.budgetCny === 'number' && typeof c.budget_cny === 'number' && ctx.budgetCny >= c.budget_cny) hits.push(`budget≥${c.budget_cny}`);
6
+ if (Array.isArray(c.best_months) && typeof ctx.month === 'number' && c.best_months.map(Number).includes(ctx.month)) hits.push(`month=${ctx.month}`);
7
+ if (hits.length === 0) return null;
8
+ return {
9
+ entry,
10
+ wishId: String(entry.wish_id ?? ''),
11
+ score: hits.length,
12
+ hits
13
+ };
14
+ }
15
+ export function pickNudgeWish(pool, ctx) {
16
+ const scored = pool.filter((e)=>!e.muted && typeof e.wish_id === 'string').map((e)=>scoreWishMatch(e, ctx)).filter((m)=>m !== null).sort((a, b)=>{
17
+ if (b.score !== a.score) return b.score - a.score;
18
+ const ta = String(a.entry.added_at ?? '');
19
+ const tb = String(b.entry.added_at ?? '');
20
+ if (ta !== tb) return ta < tb ? -1 : 1;
21
+ return a.wishId < b.wishId ? -1 : 1;
22
+ });
23
+ return scored[0] ?? null;
24
+ }
25
+
26
+
27
+ //# sourceURL=/Users/bytedance/work/gotry/ts/src/wish-pool.ts
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@danceiny/gotry",
3
+ "version": "0.0.1-rc.10",
4
+ "description": "GoTry — 从出发到下一次出发的 AI 旅行 Agent(dsh 插件)。npm 包入口 + vendored dsh runtime + 5 行 README 安装路径。",
5
+ "type": "module",
6
+ "main": "ts/src/index.ts",
7
+ "bin": {
8
+ "gotry": "bin/gotry-inner.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "data/*.json",
13
+ "bin/",
14
+ "ts/src/index.ts",
15
+ "ts/src/contracts.ts",
16
+ "ts/src/unified.ts",
17
+ "ts/src/model.ts",
18
+ "ts/src/bridge.ts",
19
+ "ts/src/loop.ts",
20
+ "ts/src/dsh-llm.ts",
21
+ "ts/src/mock-llm.ts",
22
+ "ts/src/state-ledger.ts",
23
+ "ts/src/tool-packet.ts",
24
+ "ts/src/memory-capture.ts",
25
+ "ts/src/memory-utility.ts",
26
+ "ts/src/memory-decay.ts",
27
+ "ts/src/wish-pool.ts",
28
+ "ts/src/travel-timeline.ts",
29
+ "ts/src/companions.ts",
30
+ "ts/src/time-anchor.ts",
31
+ "ts/src/slot-spec.ts",
32
+ "ts/src/travel-slots.ts",
33
+ "ts/scripts/skeleton-check.ts",
34
+ "ts/scripts/skeleton-integration-test.ts",
35
+ "ts/scripts/async-collect.ts",
36
+ "ts/scripts/state-cli.ts",
37
+ "ts/capabilities/hbcli.ts",
38
+ "ts/capabilities/incident-log.ts",
39
+ "ts/capabilities/agent-reach.ts",
40
+ "ts/capabilities/agent-reach-deep.ts",
41
+ "ts/capabilities/anything.ts",
42
+ "ts/capabilities/weather.ts",
43
+ "ts/capabilities/opensky.ts",
44
+ "ts/capabilities/flyai.ts",
45
+ "ts/capabilities/session-search.ts",
46
+ "ts/capabilities/session/",
47
+ "ts/package.json",
48
+ "cordis.gotry-patch.yml",
49
+ "README.md",
50
+ "LICENSE"
51
+ ],
52
+ "engines": {
53
+ "node": ">=22.0.0"
54
+ },
55
+ "scripts": {
56
+ "postinstall": "echo 'GoTry installed. Run: npx gotry web (dsh Web UI on :3080)\\nSee: https://github.com/Danceiny/gotry'",
57
+ "test": "bash scripts/run-all-tests.sh"
58
+ },
59
+ "dependencies": {
60
+ "@deepseek-ai/dsh": "^0.1.1-rc.1",
61
+ "@deepseek-ai/dsh-settings": "0.1.1-rc.2",
62
+ "@deepseek-ai/dsh-tools": "0.1.1-rc.2",
63
+ "@deepseek-ai/schemastery": "^3.18.1",
64
+ "better-sqlite3": "^13.0.3",
65
+ "dsh-calendar": "^0.3.2",
66
+ "dsh-map-tools": "^0.4.4",
67
+ "puppeteer-core": "^25.9.0",
68
+ "z3-solver": "^5.2.0",
69
+ "@deepseek-ai/dsh-scope": "0.1.1-rc.2"
70
+ },
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "git+https://github.com/Danceiny/gotry.git"
74
+ },
75
+ "keywords": [
76
+ "ai",
77
+ "agent",
78
+ "travel",
79
+ "z3",
80
+ "dsh",
81
+ "deepseek-harness"
82
+ ],
83
+ "license": "MIT"
84
+ }