@danceiny/gotry 0.0.1-rc.10 → 0.0.1-rc.12

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 (48) hide show
  1. package/README.md +35 -24
  2. package/cordis.gotry-patch.yml +14 -7
  3. package/dist/capabilities/flyai.js +135 -30
  4. package/dist/capabilities/hbcli.js +14 -2
  5. package/dist/capabilities/session/benchmark.js +252 -0
  6. package/dist/capabilities/session/transport.js +14 -9
  7. package/dist/capabilities/session-consent.js +82 -0
  8. package/dist/capabilities/session-login.js +103 -0
  9. package/dist/capabilities/session-search.js +5 -3
  10. package/dist/capabilities/weather.js +72 -17
  11. package/dist/scripts/agent-reach-wrapper-tests.js +4 -0
  12. package/dist/scripts/async-collect.js +33 -5
  13. package/dist/scripts/hbcli-tests.js +25 -4
  14. package/dist/scripts/i18n-tests.js +34 -0
  15. package/dist/scripts/ledger-tests.js +134 -4
  16. package/dist/scripts/memory-value-report.js +379 -0
  17. package/dist/scripts/product-metrics.js +569 -0
  18. package/dist/scripts/realtime-pricing-tests.js +115 -0
  19. package/dist/scripts/replay-real.js +3 -1
  20. package/dist/scripts/session-benchmark.js +261 -0
  21. package/dist/scripts/session-login.js +28 -0
  22. package/dist/scripts/session-tests.js +263 -22
  23. package/dist/scripts/smoke.js +112 -14
  24. package/dist/scripts/weather-tests.js +8 -1
  25. package/dist/scripts/z3-race-tests.js +35 -0
  26. package/dist/src/engine.js +185 -116
  27. package/dist/src/i18n.js +77 -0
  28. package/dist/src/index.js +124 -5
  29. package/dist/src/journey.js +78 -82
  30. package/dist/src/loop.js +65 -14
  31. package/dist/src/realtime-pricing.js +105 -0
  32. package/dist/src/state-ledger.js +30 -4
  33. package/dist/src/unified.js +158 -105
  34. package/dist/src/z3-shared.js +18 -0
  35. package/package.json +5 -3
  36. package/ts/capabilities/flyai.ts +177 -22
  37. package/ts/capabilities/hbcli.ts +16 -4
  38. package/ts/capabilities/session/benchmark.ts +273 -0
  39. package/ts/capabilities/session/transport.ts +19 -8
  40. package/ts/capabilities/session-consent.ts +127 -0
  41. package/ts/capabilities/session-login.ts +130 -0
  42. package/ts/capabilities/session-search.ts +11 -4
  43. package/ts/capabilities/weather.ts +84 -19
  44. package/ts/scripts/async-collect.ts +48 -7
  45. package/ts/src/index.ts +107 -11
  46. package/ts/src/loop.ts +85 -12
  47. package/ts/src/state-ledger.ts +48 -4
  48. package/ts/src/unified.ts +40 -36
@@ -0,0 +1,105 @@
1
+ import { flyaiSearch } from '../capabilities/flyai.js';
2
+ const IATA_TO_CITY = {
3
+ HKG: '香港',
4
+ HKT: '普吉',
5
+ BKK: '曼谷',
6
+ KMG: '昆明',
7
+ SZX: '深圳',
8
+ DXB: '迪拜',
9
+ LJG: '丽江',
10
+ DLU: '大理',
11
+ SHA: '上海',
12
+ PVG: '上海',
13
+ PEK: '北京',
14
+ CTU: '成都'
15
+ };
16
+ export async function overlayRealtimeFlightPrices(spec, opts = {}) {
17
+ const out = {
18
+ spec,
19
+ matched: [],
20
+ notes: []
21
+ };
22
+ const flights = [];
23
+ for (const seg of spec.segments){
24
+ if (!seg.date || !/^\d{4}-\d{2}-\d{2}$/.test(seg.date)) continue;
25
+ const route = seg.route;
26
+ if (!route || !route.includes('->')) continue;
27
+ const [a, b] = route.split('->').map((s)=>IATA_TO_CITY[s.trim()]);
28
+ if (!a || !b) continue;
29
+ if (seg.options.some((o)=>!o.move)) continue;
30
+ flights.push({
31
+ seg,
32
+ date: seg.date,
33
+ origin: a,
34
+ destination: b
35
+ });
36
+ }
37
+ if (flights.length === 0) return out;
38
+ const query = opts.query ?? defaultQuery;
39
+ for (const f of flights){
40
+ let result;
41
+ try {
42
+ result = await query({
43
+ kind: 'flight',
44
+ origin: f.origin,
45
+ destination: f.destination,
46
+ depDate: f.date
47
+ });
48
+ } catch (e) {
49
+ out.notes.push(`${f.seg.id}: 实时票价不可用(降级静态包):${e.message?.slice(0, 80)} [实时API:flyai@error]`);
50
+ continue;
51
+ }
52
+ if (!result.ok || result.verdict !== 'hit' || !result.options?.length) {
53
+ out.notes.push(`${f.seg.id}: 实时票价未取回(${result.verdict},降级静态包) ${result.evidence}`);
54
+ continue;
55
+ }
56
+ const ts = /@(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]/.exec(result.evidence)?.[1] ?? new Date().toISOString();
57
+ const byNo = new Map();
58
+ for (const o of result.options){
59
+ if (Number.isFinite(o.price) && o.price > 0) byNo.set(o.no.toUpperCase(), o);
60
+ }
61
+ for (const opt of f.seg.options){
62
+ const svc = opt.move.services[0];
63
+ const rt = byNo.get(svc.id.toUpperCase());
64
+ if (!rt) continue;
65
+ const evidence = `[实时API:flyai@${ts}] ${rt.no} ¥${rt.price}(静态包原价 ¥${svc.priceCny})`;
66
+ out.matched.push({
67
+ segment: f.seg.id,
68
+ option: opt.id,
69
+ staticCny: svc.priceCny,
70
+ realtimeCny: rt.price,
71
+ evidence
72
+ });
73
+ out.notes.push(evidence);
74
+ svc.priceCny = rt.price;
75
+ }
76
+ }
77
+ return out;
78
+ }
79
+ const defaultQuery = (q)=>flyaiSearch({
80
+ kind: q.kind,
81
+ origin: q.origin,
82
+ destination: q.destination,
83
+ depDate: q.depDate
84
+ });
85
+ export function realtimeSolvePort(base, opts = {}) {
86
+ return async (spec)=>{
87
+ const enabled = opts.enabled ?? process.env['GOTRY_REALTIME_PRICING'] === '1';
88
+ if (!enabled) return base(spec);
89
+ try {
90
+ const ov = await overlayRealtimeFlightPrices(spec, opts);
91
+ if (ov.matched.length === 0 && ov.notes.length === 0) return base(ov.spec);
92
+ const res = await base(ov.spec);
93
+ res.skeleton_notes = [
94
+ ...res.skeleton_notes ?? [],
95
+ ...ov.notes
96
+ ];
97
+ return res;
98
+ } catch {
99
+ return base(spec);
100
+ }
101
+ };
102
+ }
103
+
104
+
105
+ //# sourceURL=/Users/bytedance/work/gotry/ts/src/realtime-pricing.ts
@@ -543,21 +543,47 @@ export class StateLedger {
543
543
  markStepDone(runId, name, result) {
544
544
  this.db.prepare(`UPDATE workflow_steps SET status = 'done', result = ?, done_ts = ? WHERE run_id = ? AND name = ? AND tenant_id = ?`).run(JSON.stringify(result), new Date().toISOString(), runId, name, this.tenant);
545
545
  }
546
- settleWorkflowRun(id, deliverable, actor = 'system:async-collect') {
546
+ finishWorkflowRun(id, status, eventKind, deliverable, terminalOutcome, actor = 'system:async-collect') {
547
547
  const run = this.db.transaction(()=>{
548
- this.db.prepare(`UPDATE workflow_runs SET status = 'settled', deliverable = ?, updated = ? WHERE id = ? AND tenant_id = ?`).run(deliverable, new Date().toISOString(), id, this.tenant);
548
+ const update = this.db.prepare(`UPDATE workflow_runs SET status = ?, deliverable = ?, updated = ?
549
+ WHERE id = ? AND tenant_id = ? AND status = 'pending'`).run(status, deliverable, new Date().toISOString(), id, this.tenant);
550
+ if (update.changes === 0) {
551
+ const existing = this.getWorkflowRun(id);
552
+ if (existing?.status === status) return;
553
+ throw new Error(`workflow ${id} cannot transition ${existing?.status ?? 'missing'} -> ${status}`);
554
+ }
549
555
  this.insertEvent({
550
556
  actor,
551
- kind: 'async.settled',
557
+ kind: eventKind,
552
558
  subjectId: id,
553
559
  payload: {
554
- bytes: deliverable.length
560
+ bytes: deliverable.length,
561
+ terminal_outcome: terminalOutcome
555
562
  },
563
+ idemKey: `async-terminal:${id}`,
556
564
  runId: id
557
565
  });
558
566
  });
559
567
  run();
560
568
  }
569
+ settleWorkflowRun(id, deliverable, terminalOutcome, actor = 'system:async-collect') {
570
+ this.finishWorkflowRun(id, 'settled', 'async.settled', deliverable, terminalOutcome, actor);
571
+ }
572
+ failWorkflowRun(id, deliverable, terminalOutcome, actor = 'system:async-collect') {
573
+ this.finishWorkflowRun(id, 'failed', 'async.failed', deliverable, terminalOutcome, actor);
574
+ }
575
+ getWorkflowTerminalOutcome(id) {
576
+ const row = this.db.prepare(`SELECT payload FROM events
577
+ WHERE tenant_id = ? AND run_id = ? AND kind IN ('async.settled', 'async.failed')
578
+ ORDER BY seq DESC LIMIT 1`).get(this.tenant, id);
579
+ if (!row) return null;
580
+ try {
581
+ const payload = JSON.parse(row.payload);
582
+ return payload.terminal_outcome ?? null;
583
+ } catch {
584
+ return null;
585
+ }
586
+ }
561
587
  requestPendingWrite(input, actor = 'system:writegate') {
562
588
  const ts = new Date().toISOString();
563
589
  const run = this.db.transaction(()=>{
@@ -1,13 +1,7 @@
1
1
  import { checkConnectivity } from '../scripts/skeleton-check.js';
2
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
- }
3
+ import { withZ3 } from './z3-shared.js';
4
+ import { t as i18nT } from './i18n.js';
11
5
  const SEGMENT_ROUTES = {
12
6
  f1: 'HKG->HKT',
13
7
  f2: 'HKT->BKK',
@@ -165,7 +159,12 @@ function workWindowBlocks(spec, option) {
165
159
  const start = ww.startMin + shift, end = ww.endMin + shift;
166
160
  if (start <= dep && dep <= end) {
167
161
  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)})内`;
162
+ return i18nT('un.workwindow_reason', {
163
+ wd: WEEKDAY_CN[option.depWeekday] ?? '',
164
+ dep: hhmm(dep),
165
+ start: hhmm(start),
166
+ end: hhmm(end)
167
+ });
169
168
  }
170
169
  return null;
171
170
  }
@@ -250,91 +249,95 @@ async function solveUnifiedInner(spec) {
250
249
  };
251
250
  }
252
251
  }
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 = [];
252
+ return withZ3('unified.solveUnifiedInner', async (z3)=>{
253
+ const { Bool, If, Int, Solver, Sum } = z3;
254
+ const wakeFloor = spec.defaultWakeFloorMin ?? hhmmToMin('06:00');
255
+ const allSels = {};
256
+ const assertions = {};
257
+ const svcOf = {};
287
258
  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)
259
+ const sels = seg.options.map((_, i)=>Bool.const(`${seg.id}_o${i}`));
260
+ allSels[seg.id] = sels;
261
+ const svcs = seg.options.map((o)=>o.move.services[0]);
262
+ svcOf[seg.id] = svcs;
263
+ const pick = (attr)=>Sum(...sels.map((s, i)=>If(s, Int.val(svcs[i][attr]), Int.val(0))));
264
+ const dep = pick('depMin'), arr = pick('arrMin');
265
+ const o0 = seg.options[0].move;
266
+ const wake = dep.sub(Int.val(o0.bufferMin + o0.originTransferMin));
267
+ const arriveStay = arr.add(Int.val(o0.destTransferMin));
268
+ if (seg.anchors?.arriveByMin !== undefined) assertions[`${seg.id}:arrive_by`] = arriveStay.le(Int.val(seg.anchors.arriveByMin));
269
+ if (seg.anchors?.departAfterMin !== undefined) assertions[`${seg.id}:depart_after`] = dep.ge(Int.val(seg.anchors.departAfterMin));
270
+ if (!seg.options.some((o)=>o.move?.redEye)) assertions[`${seg.id}:wake_floor`] = wake.ge(Int.val(wakeFloor));
271
+ }
272
+ 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)))));
273
+ if (spec.budgetCny !== undefined) assertions['total:budget'] = total.le(Int.val(spec.budgetCny));
274
+ const exactlyOne = (sels)=>Sum(...sels.map((s)=>If(s, Int.val(1), Int.val(0)))).eq(Int.val(1));
275
+ const coreOf = (s)=>{
276
+ const v = s.unsatCore();
277
+ const out = [];
278
+ for(let i = 0; i < v.length(); i++)out.push(String(v.get(i)).replace(/^\|/, '').replace(/\|$/, ''));
279
+ return out;
280
+ };
281
+ const s = new Solver();
282
+ for (const sel of Object.values(allSels))s.add(exactlyOne(sel));
283
+ for (const [name, expr] of Object.entries(assertions))s.addAndTrack(expr, name);
284
+ const report = async (model)=>{
285
+ const out = [];
286
+ for (const seg of spec.segments){
287
+ for(let i = 0; i < allSels[seg.id].length; i++){
288
+ const v = await maybeAwait(model.eval(allSels[seg.id][i], true));
289
+ if (String(v) !== 'true') continue;
290
+ const o = seg.options[i];
291
+ out.push({
292
+ leg: seg.id,
293
+ ...evaluateOptionMove(seg.id, o.move)
294
+ });
295
+ break;
296
+ }
297
+ }
298
+ return out;
299
+ };
300
+ async function maybeAwait(v) {
301
+ return v instanceof Promise ? await v : v;
302
+ }
303
+ if (String(await s.check()) !== 'unsat') {
304
+ const legs = await report(s.model());
305
+ const money = legs.reduce((a, l)=>a + l.price_cny, 0);
306
+ 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)=>i18nT('un.redflag_redeye', {
307
+ leg: l.leg,
308
+ pct: l.energy_pct
309
+ }));
310
+ return {
311
+ feasible: true,
312
+ money_cny: money,
313
+ legs,
314
+ red_flags: redFlags,
315
+ work_window_exclusions: exclusions,
316
+ skeleton_notes: skeletonNotes.length ? skeletonNotes : undefined
317
+ };
318
+ }
319
+ const core = coreOf(s).sort();
320
+ const suggestions = [];
321
+ for (const name of core){
322
+ const s2 = new Solver();
323
+ for (const sel of Object.values(allSels))s2.add(exactlyOne(sel));
324
+ for (const [n2, expr] of Object.entries(assertions)){
325
+ if (n2 !== name) s2.addAndTrack(expr, n2);
326
+ }
327
+ if (String(await s2.check()) !== 'unsat') {
328
+ const legs = await report(s2.model());
329
+ suggestions.push({
330
+ relax: name,
331
+ money_cny: legs.reduce((a, l)=>a + l.price_cny, 0)
295
332
  });
296
- break;
297
333
  }
298
334
  }
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
335
  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
336
+ feasible: false,
337
+ unsat_core: core,
338
+ suggestions
315
339
  };
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
- };
340
+ });
338
341
  }
339
342
  function checkTrueCost(t, spec) {
340
343
  const fails = [];
@@ -459,7 +462,10 @@ export function solveChoiceSegment(spec, req) {
459
462
  wish = {
460
463
  name: opt.label,
461
464
  conditions: conds,
462
- reason: `${windowDays} 天窗口装不下(目的需 ${minDays} 天)`
465
+ reason: i18nT('sg.wish_reason_window', {
466
+ days: windowDays,
467
+ minDays
468
+ })
463
469
  };
464
470
  }
465
471
  verdicts.push({
@@ -483,52 +489,99 @@ function renderCandidateMarkdown(req, verdicts, recommended) {
483
489
  const feasible = verdicts.filter((v)=>v['feasible']).sort((a, b)=>b['imagery_match'] - a['imagery_match']);
484
490
  const parked = verdicts.filter((v)=>!v['feasible']);
485
491
  const lines = [];
486
- lines.push(`> 憧憬:${req.note}`);
487
- lines.push(`> 已识别约束:窗口 ${req.windowDays} 天 | 预算 ¥${req.budgetCny} | ` + `动机(休整改写需求 ${requiredUsableHours(req.motivation).toFixed(1)}h 有效休整)`);
492
+ lines.push(i18nT('md.header', {
493
+ note: req.note
494
+ }));
495
+ lines.push(i18nT('md.constraints', {
496
+ days: req.windowDays,
497
+ budget: req.budgetCny,
498
+ hours: requiredUsableHours(req.motivation).toFixed(1)
499
+ }));
488
500
  lines.push('');
489
501
  for (const v of parked){
490
502
  const core = v['unsat_core'] ?? [];
491
- lines.push(`**${v['name']}:现在不行**——冲突约束:${core.join('、')}。`);
503
+ lines.push(i18nT('md.infeasible', {
504
+ name: String(v['name']),
505
+ core: core.join('、')
506
+ }));
492
507
  const sug = (v['suggestions'] ?? [])[0];
493
- if (sug) lines.push(`- 放宽 ${sug['relax']}:约 ¥${sug['resulting_money_cny']}`);
508
+ if (sug) lines.push(i18nT('md.relax_one', {
509
+ relax: String(sug['relax']),
510
+ money: String(sug['resulting_money_cny'] ?? '')
511
+ }));
494
512
  const wish = v['wish_pool'];
495
513
  if (wish) {
496
514
  const conds = wish['conditions'];
497
- const budgetNote = conds['budget_cny'] ? `、约 ¥${conds['budget_cny']}` : '';
515
+ const budgetNote = conds['budget_cny'] ? i18nT('md.wish_budget', {
516
+ cny: String(conds['budget_cny'])
517
+ }) : '';
498
518
  const months = conds['best_months'];
499
- const season = months ? `,${months} 月最佳` : '';
500
- lines.push(`- 已放入「下一次出发」清单:需要 ${conds['days']} 天${budgetNote}${season}`);
519
+ const season = months ? i18nT('md.wish_season', {
520
+ months: String(months)
521
+ }) : '';
522
+ lines.push(i18nT('md.wish_pool', {
523
+ days: String(conds['days']),
524
+ budgetNote,
525
+ season
526
+ }));
501
527
  }
502
528
  }
503
529
  lines.push('');
504
530
  for (const v of feasible){
505
531
  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']})`);
532
+ const tc = v['true_cost'];
533
+ lines.push(i18nT('md.feasible_unified', {
534
+ name: String(v['name']),
535
+ svc: String(ch['out_service']),
536
+ mode: String(ch['out_transfer']),
537
+ wake: String(tc['wake']),
538
+ energy: String(tc['energy_arrival_pct']),
539
+ d2d: String(tc['door_to_door_out']),
540
+ hours: String(tc['usable_hours']),
541
+ money: String(tc['money_cny'])
542
+ }));
508
543
  }
509
544
  if (feasible.length) {
510
545
  lines.push('');
511
546
  const best = feasible[0];
512
547
  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)}%)。` : ''));
548
+ lines.push(i18nT('md.recommend', {
549
+ best: String(best['name']),
550
+ match: (best['imagery_match'] * 100).toFixed(0)
551
+ }) + (alt ? i18nT('md.alt', {
552
+ name: String(alt['name']),
553
+ money: String(alt['true_cost']['money_cny']),
554
+ match: (alt['imagery_match'] * 100).toFixed(0)
555
+ }) : ''));
514
556
  }
515
557
  lines.push('');
516
- lines.push('**待你决定的两个问题**:');
558
+ lines.push(i18nT('md.decide_two'));
517
559
  if (feasible.length >= 2) {
518
- lines.push(`1. ${feasible[0]['name']} 还是 ${feasible[1]['name']}?(前者更贴意象,后者更省)`);
560
+ lines.push(i18nT('md.q_choice', {
561
+ a: String(feasible[0]['name']),
562
+ b: String(feasible[1]['name'])
563
+ }));
519
564
  } else if (feasible.length === 1) {
520
- lines.push(`1. 就去 ${feasible[0]['name']} 吗?`);
565
+ lines.push(i18nT('md.q_single', {
566
+ name: String(feasible[0]['name'])
567
+ }));
521
568
  } else {
522
- lines.push('1. 所有候选都不可行——考虑放宽哪条约束?');
569
+ lines.push(i18nT('md.q_none'));
523
570
  }
524
571
  const p0 = parked[0];
525
572
  const p0wish = p0?.['wish_pool'];
526
573
  if (p0wish) {
527
574
  const conds = p0wish['conditions'];
528
- lines.push(`2. 把 ${p0['name']} 留给「下一次出发」(${conds['days']} 天起),这次先去可行的?`);
575
+ lines.push(i18nT('md.q_wish', {
576
+ name: String(p0['name']),
577
+ days: String(conds['days'])
578
+ }));
529
579
  } else if (feasible.length) {
530
580
  const ch = feasible[0]['chosen'];
531
- lines.push(`2. 出发班次选 ${ch['out_service']} 还是更晚的?`);
581
+ lines.push(i18nT('md.q_depart', {
582
+ id: String(ch['out_service']),
583
+ dep: ''
584
+ }));
532
585
  }
533
586
  return lines.join('\n');
534
587
  }
@@ -0,0 +1,18 @@
1
+ let z3Promise = null;
2
+ let chain = Promise.resolve();
3
+ export async function getZ3() {
4
+ if (!z3Promise) {
5
+ const { init } = await import('z3-solver');
6
+ z3Promise = (async ()=>(await init()).Context('main'))();
7
+ }
8
+ return z3Promise;
9
+ }
10
+ export async function withZ3(label, session) {
11
+ const z3 = await getZ3();
12
+ const turn = chain.then(()=>session(z3));
13
+ chain = turn.then(()=>undefined, ()=>undefined);
14
+ return turn;
15
+ }
16
+
17
+
18
+ //# sourceURL=/Users/bytedance/work/gotry/ts/src/z3-shared.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danceiny/gotry",
3
- "version": "0.0.1-rc.10",
3
+ "version": "0.0.1-rc.12",
4
4
  "description": "GoTry — 从出发到下一次出发的 AI 旅行 Agent(dsh 插件)。npm 包入口 + vendored dsh runtime + 5 行 README 安装路径。",
5
5
  "type": "module",
6
6
  "main": "ts/src/index.ts",
@@ -47,7 +47,9 @@
47
47
  "ts/package.json",
48
48
  "cordis.gotry-patch.yml",
49
49
  "README.md",
50
- "LICENSE"
50
+ "LICENSE",
51
+ "ts/capabilities/session-consent.ts",
52
+ "ts/capabilities/session-login.ts"
51
53
  ],
52
54
  "engines": {
53
55
  "node": ">=22.0.0"
@@ -81,4 +83,4 @@
81
83
  "deepseek-harness"
82
84
  ],
83
85
  "license": "MIT"
84
- }
86
+ }