@ai-ecoverse/slicc-spectrum 1.92.0 → 1.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/chunk-RYA3OPGE.js +1885 -0
- package/dist/chunk-RYA3OPGE.js.map +7 -0
- package/dist/slicc-dummy.js +133 -1621
- package/dist/slicc-dummy.js.map +4 -4
- package/dist/slicc-kernel-model.js +1 -1
- package/dist/slicc-ui.js +732 -64
- package/dist/slicc-ui.js.map +4 -4
- package/dist/types/app/approvals.d.ts +1 -1
- package/dist/types/app/chat.d.ts +2 -1
- package/dist/types/app/confirm.d.ts +4 -3
- package/dist/types/app/freezer-import.d.ts +1 -1
- package/dist/types/app/guests.d.ts +1 -1
- package/dist/types/app/secrets.d.ts +18 -2
- package/dist/types/app/settings.d.ts +25 -1
- package/dist/types/app/sign-in.d.ts +12 -0
- package/dist/types/app/value-request.d.ts +1 -1
- package/dist/types/dummy/index.d.ts +1 -1
- package/dist/types/dummy/providers.d.ts +4 -0
- package/dist/types/dummy/settings.d.ts +4 -2
- package/dist/types/dummy/vault.d.ts +6 -2
- package/dist/types/dummy.d.ts +2 -1
- package/dist/types/model/types.d.ts +52 -2
- package/package.json +1 -1
- package/dist/chunk-3CM23WFV.js +0 -122
- package/dist/chunk-3CM23WFV.js.map +0 -7
package/dist/slicc-dummy.js
CHANGED
|
@@ -5,8 +5,26 @@ import {
|
|
|
5
5
|
} from "./chunk-XQHRTHDQ.js";
|
|
6
6
|
import {
|
|
7
7
|
DummySettings,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
accounts,
|
|
9
|
+
agents,
|
|
10
|
+
clock,
|
|
11
|
+
conversations,
|
|
12
|
+
defaults,
|
|
13
|
+
directories,
|
|
14
|
+
files,
|
|
15
|
+
laterModels,
|
|
16
|
+
models,
|
|
17
|
+
mountPoint,
|
|
18
|
+
mounted,
|
|
19
|
+
pending,
|
|
20
|
+
providerAccount,
|
|
21
|
+
providerAccounts,
|
|
22
|
+
providerFixtures,
|
|
23
|
+
queues,
|
|
24
|
+
skillEdit,
|
|
25
|
+
tabs,
|
|
26
|
+
terminals
|
|
27
|
+
} from "./chunk-RYA3OPGE.js";
|
|
10
28
|
import {
|
|
11
29
|
Emitter
|
|
12
30
|
} from "./chunk-T5NHW4KC.js";
|
|
@@ -196,23 +214,23 @@ function session(now2) {
|
|
|
196
214
|
const harbor2 = { tabId: "tab-preview", agentId: "cone-harbor" };
|
|
197
215
|
return Array.from({ length: 170 }, (_, i) => {
|
|
198
216
|
const city = cities[i % cities.length];
|
|
199
|
-
const
|
|
217
|
+
const at = now2 - 286 * second + i * 960;
|
|
200
218
|
switch (i % 5) {
|
|
201
219
|
case 0:
|
|
202
220
|
return {
|
|
203
221
|
...harbor2,
|
|
204
222
|
kind: "goto",
|
|
205
223
|
value: `http://localhost:8787/forecast?city=${city}&days=${i % 7 + 1}&units=metric&compare=tides,wind,swell&session=run-${i}`,
|
|
206
|
-
at
|
|
224
|
+
at
|
|
207
225
|
};
|
|
208
226
|
case 1:
|
|
209
|
-
return { ...harbor2, kind: "snapshot", at
|
|
227
|
+
return { ...harbor2, kind: "snapshot", at };
|
|
210
228
|
case 2:
|
|
211
|
-
return { ...harbor2, kind: "click", target: `button "Day ${i % 7 + 1}"`, at
|
|
229
|
+
return { ...harbor2, kind: "click", target: `button "Day ${i % 7 + 1}"`, at };
|
|
212
230
|
case 3:
|
|
213
|
-
return { ...harbor2, kind: "scroll", at
|
|
231
|
+
return { ...harbor2, kind: "scroll", at };
|
|
214
232
|
default:
|
|
215
|
-
return { ...harbor2, kind: "screenshot", at
|
|
233
|
+
return { ...harbor2, kind: "screenshot", at };
|
|
216
234
|
}
|
|
217
235
|
});
|
|
218
236
|
}
|
|
@@ -327,9 +345,9 @@ var DummyBrowser = class extends Emitter {
|
|
|
327
345
|
this.emit("action", { ...action });
|
|
328
346
|
}
|
|
329
347
|
async run(script2) {
|
|
330
|
-
for (const [
|
|
348
|
+
for (const [index, draft] of script2.entries()) {
|
|
331
349
|
const id = this.record(draft);
|
|
332
|
-
if (
|
|
350
|
+
if (index === script2.length - 1) return;
|
|
333
351
|
await this.#clock.sleep(20);
|
|
334
352
|
this.finish(id);
|
|
335
353
|
await this.#clock.sleep(10);
|
|
@@ -405,11 +423,11 @@ var DummyBrowser = class extends Emitter {
|
|
|
405
423
|
this.#load(id);
|
|
406
424
|
}
|
|
407
425
|
close(id) {
|
|
408
|
-
const
|
|
409
|
-
if (
|
|
410
|
-
this.#tabs.splice(
|
|
426
|
+
const index = this.#tabs.findIndex((tab) => tab.id === id);
|
|
427
|
+
if (index < 0) return;
|
|
428
|
+
this.#tabs.splice(index, 1);
|
|
411
429
|
if (this.#active === id)
|
|
412
|
-
this.#active = this.#tabs[Math.min(
|
|
430
|
+
this.#active = this.#tabs[Math.min(index, this.#tabs.length - 1)]?.id ?? null;
|
|
413
431
|
this.#changed();
|
|
414
432
|
}
|
|
415
433
|
async screenshot(id) {
|
|
@@ -513,1558 +531,6 @@ var DummyFollow = class extends Emitter {
|
|
|
513
531
|
}
|
|
514
532
|
};
|
|
515
533
|
|
|
516
|
-
// src/dummy/showcase.ts
|
|
517
|
-
var start = Date.UTC(2026, 9, 4, 16, 0);
|
|
518
|
-
var at = (minutes2) => start + minutes2 * 6e4;
|
|
519
|
-
function picture(label2, from, to, width = 640, height = 400) {
|
|
520
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="${from}"/><stop offset="1" stop-color="${to}"/></linearGradient></defs><rect width="${width}" height="${height}" fill="url(#g)"/><text x="50%" y="50%" font-family="sans-serif" font-size="${Math.round(height / 9)}" fill="#ffffff" text-anchor="middle" dominant-baseline="middle">${label2}</text></svg>`;
|
|
521
|
-
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
522
|
-
}
|
|
523
|
-
function tone(seconds = 0.6, frequency = 440, rate = 8e3) {
|
|
524
|
-
const samples = Math.round(seconds * rate);
|
|
525
|
-
const bytes = new Uint8Array(44 + samples);
|
|
526
|
-
const view = new DataView(bytes.buffer);
|
|
527
|
-
const text = (offset, value) => {
|
|
528
|
-
for (let i = 0; i < value.length; i++) bytes[offset + i] = value.charCodeAt(i);
|
|
529
|
-
};
|
|
530
|
-
text(0, "RIFF");
|
|
531
|
-
view.setUint32(4, 36 + samples, true);
|
|
532
|
-
text(8, "WAVEfmt ");
|
|
533
|
-
view.setUint32(16, 16, true);
|
|
534
|
-
view.setUint16(20, 1, true);
|
|
535
|
-
view.setUint16(22, 1, true);
|
|
536
|
-
view.setUint32(24, rate, true);
|
|
537
|
-
view.setUint32(28, rate, true);
|
|
538
|
-
view.setUint16(32, 1, true);
|
|
539
|
-
view.setUint16(34, 8, true);
|
|
540
|
-
text(36, "data");
|
|
541
|
-
view.setUint32(40, samples, true);
|
|
542
|
-
for (let i = 0; i < samples; i++) {
|
|
543
|
-
const fade = Math.min(1, (samples - i) / (rate / 10));
|
|
544
|
-
bytes[44 + i] = 128 + Math.round(60 * fade * Math.sin(2 * Math.PI * frequency * i / rate));
|
|
545
|
-
}
|
|
546
|
-
let binary = "";
|
|
547
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
548
|
-
return `data:audio/wav;base64,${btoa(binary)}`;
|
|
549
|
-
}
|
|
550
|
-
function tool(id, name2, title, input, output, extra = {}) {
|
|
551
|
-
return { id, name: name2, title, input, output, status: "done", paths: [], ...extra };
|
|
552
|
-
}
|
|
553
|
-
var markdown = [
|
|
554
|
-
"## The forecast API in one page",
|
|
555
|
-
"",
|
|
556
|
-
"harbor answers `GET /forecast?city=\u2026` with the day\u2019s **high**, **low** and a *short summary*, and caches per city for an hour. ~~XML output~~ is gone.",
|
|
557
|
-
"",
|
|
558
|
-
"### How a request flows",
|
|
559
|
-
"",
|
|
560
|
-
"1. The worker parses the city.",
|
|
561
|
-
"2. The cache answers if it has a fresh entry:",
|
|
562
|
-
" - same UTC day",
|
|
563
|
-
" - younger than one hour",
|
|
564
|
-
"3. Otherwise the upstream call runs, with three retries.",
|
|
565
|
-
"",
|
|
566
|
-
"> Upstream is slow at the top of the hour. Retries back off at 250, 500 and 1000 ms.",
|
|
567
|
-
"",
|
|
568
|
-
"```ts",
|
|
569
|
-
"export async function forecast(city: string): Promise<Forecast> {",
|
|
570
|
-
" const cached = cache.get(city);",
|
|
571
|
-
" if (cached) return cached;",
|
|
572
|
-
" return cache.set(city, await retry(() => upstream(city)));",
|
|
573
|
-
"}",
|
|
574
|
-
"```",
|
|
575
|
-
"",
|
|
576
|
-
"| Route | Cache | Typical latency |",
|
|
577
|
-
"| --- | --- | ---: |",
|
|
578
|
-
"| `/forecast` | 1 hour, per city | 38 ms |",
|
|
579
|
-
"| `/health` | none | 2 ms |",
|
|
580
|
-
"| `/forecast` (miss) | \u2014 | 410 ms |",
|
|
581
|
-
"",
|
|
582
|
-
"- [x] TTL fix merged",
|
|
583
|
-
"- [ ] Move the cache to KV",
|
|
584
|
-
"",
|
|
585
|
-
"Details are in the [API reference](https://api.example.com/docs/v2/forecast). A long path that must wrap: `/workspace/harbor/node_modules/.cache/wrangler/state/v3/cache/miniflare-CacheObject/7c1f0e3b9a5d4e2f8b6a1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f.sqlite`."
|
|
586
|
-
].join("\n");
|
|
587
|
-
var toolShapes = [
|
|
588
|
-
tool("k-t1", "bash", "Count the routes", "rg -c 'pathname ===' src", "src/index.ts:2"),
|
|
589
|
-
tool(
|
|
590
|
-
"k-t2",
|
|
591
|
-
"read_file",
|
|
592
|
-
"Read the router",
|
|
593
|
-
"/workspace/harbor/src/index.ts",
|
|
594
|
-
"import { forecast } from './routes/forecast.ts';\n\u2026",
|
|
595
|
-
{
|
|
596
|
-
paths: ["/workspace/harbor/src/index.ts"]
|
|
597
|
-
}
|
|
598
|
-
),
|
|
599
|
-
tool(
|
|
600
|
-
"k-t3",
|
|
601
|
-
"write_file",
|
|
602
|
-
"Create the KV binding notes",
|
|
603
|
-
"/workspace/harbor/docs/kv.md",
|
|
604
|
-
"Wrote 18 lines",
|
|
605
|
-
{
|
|
606
|
-
paths: ["/workspace/harbor/docs/kv.md"]
|
|
607
|
-
}
|
|
608
|
-
),
|
|
609
|
-
tool(
|
|
610
|
-
"k-t4",
|
|
611
|
-
"edit_file",
|
|
612
|
-
"Rename the cache key",
|
|
613
|
-
"/workspace/harbor/src/lib/cache.ts",
|
|
614
|
-
'No match for "cityKey(" in src/lib/cache.ts',
|
|
615
|
-
{
|
|
616
|
-
status: "error",
|
|
617
|
-
paths: ["/workspace/harbor/src/lib/cache.ts"]
|
|
618
|
-
}
|
|
619
|
-
),
|
|
620
|
-
tool(
|
|
621
|
-
"k-t5",
|
|
622
|
-
"grep",
|
|
623
|
-
"Find TODOs",
|
|
624
|
-
'TODO --glob "src/**"',
|
|
625
|
-
"src/routes/forecast.ts:14: TODO: cache negative results\nsrc/lib/units.ts:3: TODO: round half to even"
|
|
626
|
-
),
|
|
627
|
-
tool(
|
|
628
|
-
"k-t6",
|
|
629
|
-
"web_fetch",
|
|
630
|
-
"Fetch the API changelog",
|
|
631
|
-
"https://api.example.com/changelog.json",
|
|
632
|
-
'{ "latest": "2.4.1", "breaking": false }'
|
|
633
|
-
),
|
|
634
|
-
tool(
|
|
635
|
-
"k-t7",
|
|
636
|
-
"browser",
|
|
637
|
-
"Screenshot the preview",
|
|
638
|
-
"screenshot http://localhost:8787/",
|
|
639
|
-
"Captured 1280\xD7800",
|
|
640
|
-
{
|
|
641
|
-
image: picture("localhost:8787", "#3b63fb", "#0a7a43", 640, 400)
|
|
642
|
-
}
|
|
643
|
-
),
|
|
644
|
-
tool(
|
|
645
|
-
"k-t8",
|
|
646
|
-
"scoop_scoop",
|
|
647
|
-
"Start a scoop for the docs",
|
|
648
|
-
"name: docs-pass\ntask: Update the forecast page to Celsius",
|
|
649
|
-
"Scoop docs-pass is running"
|
|
650
|
-
),
|
|
651
|
-
tool(
|
|
652
|
-
"k-t9",
|
|
653
|
-
"feed_scoop",
|
|
654
|
-
"Hand the screenshots to docs-pass",
|
|
655
|
-
"scoop_name: docs-pass\nmessage: Use the new screenshots in /tmp/shots",
|
|
656
|
-
"Delivered"
|
|
657
|
-
),
|
|
658
|
-
tool(
|
|
659
|
-
"k-t10",
|
|
660
|
-
"send_message",
|
|
661
|
-
"Tell quiet-otter the tests pass",
|
|
662
|
-
"to: quiet-otter\nmessage: CI is green, merge when ready",
|
|
663
|
-
"Delivered"
|
|
664
|
-
),
|
|
665
|
-
tool(
|
|
666
|
-
"k-t11",
|
|
667
|
-
"drop_scoop",
|
|
668
|
-
"Wrap up amber-heron",
|
|
669
|
-
"scoop_name: amber-heron",
|
|
670
|
-
"Dropped amber-heron; its notes are in /shared/notes"
|
|
671
|
-
),
|
|
672
|
-
tool(
|
|
673
|
-
"k-t12",
|
|
674
|
-
"update_global_memory",
|
|
675
|
-
"Remember the cache rule",
|
|
676
|
-
"Cache keys are per city and per UTC day.",
|
|
677
|
-
"Memory updated (3 sections, 412 characters)"
|
|
678
|
-
),
|
|
679
|
-
tool(
|
|
680
|
-
"k-mcp",
|
|
681
|
-
"mcp__docs__search",
|
|
682
|
-
"docs \xB7 search",
|
|
683
|
-
'{\n "query": "forecast units"\n}',
|
|
684
|
-
"2 pages: /forecast, /units"
|
|
685
|
-
),
|
|
686
|
-
tool("k-t13", "bash", "Deploy to staging", "wrangler deploy --env staging", "Stopped", {
|
|
687
|
-
status: "cancelled"
|
|
688
|
-
}),
|
|
689
|
-
tool("k-t14", "bash", "Watch the logs", "wrangler tail --env staging", "", { status: "running" }),
|
|
690
|
-
tool(
|
|
691
|
-
"k-t16",
|
|
692
|
-
"bash",
|
|
693
|
-
"Check the staging routes",
|
|
694
|
-
"wrangler routes list --env staging",
|
|
695
|
-
"ROUTE ZONE WORKER ENV\nforecast.harbor.example/v2/forecast/* harbor.example harbor-forecast-v2 staging\nerror: the route forecast.harbor.example/v2/units/* belongs to another account; ask its owner to release it first",
|
|
696
|
-
{
|
|
697
|
-
status: "error",
|
|
698
|
-
notes: [
|
|
699
|
-
{ severity: "error", text: "Exited with code 5" },
|
|
700
|
-
{
|
|
701
|
-
severity: "warn",
|
|
702
|
-
text: "Output truncated: 12 lines and 1,840 bytes dropped from the start"
|
|
703
|
-
},
|
|
704
|
-
{ severity: "info", text: "Full output in /tmp/pi-bash-7f3a.log" }
|
|
705
|
-
]
|
|
706
|
-
}
|
|
707
|
-
)
|
|
708
|
-
];
|
|
709
|
-
var licks = [
|
|
710
|
-
[
|
|
711
|
-
"webhook",
|
|
712
|
-
"GitHub \xB7 pull_request.opened",
|
|
713
|
-
"example/harbor#58 Add retries for upstream timeouts",
|
|
714
|
-
'{\n "action": "opened",\n "number": 58,\n "repository": { "full_name": "example/harbor" }\n}',
|
|
715
|
-
{}
|
|
716
|
-
],
|
|
717
|
-
[
|
|
718
|
-
"cron",
|
|
719
|
-
"Every weekday 09:00",
|
|
720
|
-
"Morning summary for the harbor cone",
|
|
721
|
-
void 0,
|
|
722
|
-
{ count: 3 }
|
|
723
|
-
],
|
|
724
|
-
[
|
|
725
|
-
"sprinkle",
|
|
726
|
-
"Release board",
|
|
727
|
-
"Button \u201CShip 1.9.0\u201D clicked",
|
|
728
|
-
'{ "button": "ship", "version": "1.9.0" }',
|
|
729
|
-
{}
|
|
730
|
-
],
|
|
731
|
-
[
|
|
732
|
-
"fswatch",
|
|
733
|
-
"/workspace/harbor/src",
|
|
734
|
-
"2 files changed",
|
|
735
|
-
"modified /workspace/harbor/src/lib/cache.ts\ncreated /workspace/harbor/src/lib/retry.ts",
|
|
736
|
-
{}
|
|
737
|
-
],
|
|
738
|
-
[
|
|
739
|
-
"session-reload",
|
|
740
|
-
"Session restored",
|
|
741
|
-
"The page reloaded; 4 cones resumed from storage",
|
|
742
|
-
void 0,
|
|
743
|
-
{}
|
|
744
|
-
],
|
|
745
|
-
[
|
|
746
|
-
"navigate",
|
|
747
|
-
"api.example.com",
|
|
748
|
-
"A page offered a SLICC handoff: open the forecast docs",
|
|
749
|
-
'{ "verb": "open", "target": "https://api.example.com/docs/v2/forecast" }',
|
|
750
|
-
{}
|
|
751
|
-
],
|
|
752
|
-
[
|
|
753
|
-
"discovery",
|
|
754
|
-
"api.example.com",
|
|
755
|
-
"Found an ai-catalog.json with 3 tools",
|
|
756
|
-
"https://api.example.com/.well-known/ai-catalog.json",
|
|
757
|
-
{}
|
|
758
|
-
],
|
|
759
|
-
["upgrade", "SLICC updated", "6.4.0 \u2192 6.5.0", void 0, {}],
|
|
760
|
-
[
|
|
761
|
-
"workflow",
|
|
762
|
-
"release-notes",
|
|
763
|
-
"Step 2 of 4 finished: collect merged pull requests",
|
|
764
|
-
void 0,
|
|
765
|
-
{}
|
|
766
|
-
],
|
|
767
|
-
["bash", "Job 3", "npm run build exited with status 0 after 41 s", void 0, {}],
|
|
768
|
-
["jshd", "collect.jsh", "Script finished: 12 pull requests since v1.8.0", void 0, {}],
|
|
769
|
-
["preview", "Preview bridge", "Connected from localhost:8787", void 0, {}],
|
|
770
|
-
[
|
|
771
|
-
"cherry",
|
|
772
|
-
"Host page",
|
|
773
|
-
"Event \u201Ccart-updated\u201D from shop.example.com",
|
|
774
|
-
'{ "items": 3, "total": 42.5 }',
|
|
775
|
-
{}
|
|
776
|
-
],
|
|
777
|
-
["scoop-notify", "docs-pass", "Finished: the forecast page now shows Celsius", void 0, {}],
|
|
778
|
-
["scoop-idle", "amber-heron", "Idle for 30 minutes", void 0, {}],
|
|
779
|
-
["scoop-wait", "quiet-otter", "The wait for CI resolved: all checks passed", void 0, {}],
|
|
780
|
-
[
|
|
781
|
-
"sudo-request",
|
|
782
|
-
"docs-pass",
|
|
783
|
-
"Wants to push to example/harbor (branch docs/celsius)",
|
|
784
|
-
"git push origin docs/celsius",
|
|
785
|
-
{ state: "pending", expiresAt: Date.now() + 10 * 6e4 }
|
|
786
|
-
],
|
|
787
|
-
[
|
|
788
|
-
"sudo-request",
|
|
789
|
-
"quiet-otter",
|
|
790
|
-
"Wants to read secrets: UPSTREAM_TOKEN",
|
|
791
|
-
void 0,
|
|
792
|
-
{ state: "confirmed" }
|
|
793
|
-
],
|
|
794
|
-
[
|
|
795
|
-
"sudo-request",
|
|
796
|
-
"amber-heron",
|
|
797
|
-
"Wants to run: rm -rf /workspace/harbor/node_modules",
|
|
798
|
-
void 0,
|
|
799
|
-
{ state: "dismissed" }
|
|
800
|
-
]
|
|
801
|
-
];
|
|
802
|
-
var attachmentsMessage = {
|
|
803
|
-
id: "k-u2",
|
|
804
|
-
role: "user",
|
|
805
|
-
text: "Here is the mockup, my notes and the old spec.",
|
|
806
|
-
createdAt: at(4),
|
|
807
|
-
attachments: [
|
|
808
|
-
{
|
|
809
|
-
id: "k-a1",
|
|
810
|
-
name: "forecast-card.png",
|
|
811
|
-
kind: "image",
|
|
812
|
-
mimeType: "image/png",
|
|
813
|
-
size: 48211,
|
|
814
|
-
url: picture("Mockup", "#f5a623", "#d0021b", 480, 320)
|
|
815
|
-
},
|
|
816
|
-
{
|
|
817
|
-
id: "k-a2",
|
|
818
|
-
name: "notes.txt",
|
|
819
|
-
kind: "text",
|
|
820
|
-
mimeType: "text/plain",
|
|
821
|
-
size: 214,
|
|
822
|
-
text: "Show \xB0C first.\nRound to whole degrees.\nKeep the summary under 60 characters."
|
|
823
|
-
},
|
|
824
|
-
{
|
|
825
|
-
id: "k-a3",
|
|
826
|
-
name: "spec-v1.pdf",
|
|
827
|
-
kind: "file",
|
|
828
|
-
mimeType: "application/pdf",
|
|
829
|
-
size: 1204331,
|
|
830
|
-
path: "/home/user/downloads/spec-v1.pdf"
|
|
831
|
-
},
|
|
832
|
-
{
|
|
833
|
-
id: "k-a4",
|
|
834
|
-
name: "UPSTREAM_TOKEN",
|
|
835
|
-
kind: "secret",
|
|
836
|
-
mimeType: "application/octet-stream",
|
|
837
|
-
size: 0
|
|
838
|
-
},
|
|
839
|
-
{
|
|
840
|
-
id: "k-a5",
|
|
841
|
-
name: "recording.mov",
|
|
842
|
-
kind: "file",
|
|
843
|
-
mimeType: "video/quicktime",
|
|
844
|
-
size: 98304e3,
|
|
845
|
-
error: "Too large: attachments are limited to 25 MB"
|
|
846
|
-
}
|
|
847
|
-
]
|
|
848
|
-
};
|
|
849
|
-
var showcaseAgent = {
|
|
850
|
-
id: "cone-kitchen",
|
|
851
|
-
name: "kitchen-sink",
|
|
852
|
-
kind: "cone",
|
|
853
|
-
parentId: null,
|
|
854
|
-
status: "idle",
|
|
855
|
-
model: "claude-opus-5-5",
|
|
856
|
-
contextFill: 0.47,
|
|
857
|
-
unread: 0
|
|
858
|
-
};
|
|
859
|
-
function opening() {
|
|
860
|
-
return [
|
|
861
|
-
{
|
|
862
|
-
id: "k-u1",
|
|
863
|
-
role: "user",
|
|
864
|
-
text: "Give me the full tour of harbor, everything you can show.",
|
|
865
|
-
createdAt: at(0)
|
|
866
|
-
},
|
|
867
|
-
{
|
|
868
|
-
id: "k-m1",
|
|
869
|
-
role: "assistant",
|
|
870
|
-
status: "done",
|
|
871
|
-
createdAt: at(1),
|
|
872
|
-
model: "claude-sonnet-5-5",
|
|
873
|
-
usage: { input: 18204, output: 912, cost: 0.31 },
|
|
874
|
-
parts: [
|
|
875
|
-
{
|
|
876
|
-
type: "thinking",
|
|
877
|
-
text: "Start with the shape of the API, then show the pieces one kind at a time."
|
|
878
|
-
},
|
|
879
|
-
{ type: "text", text: markdown }
|
|
880
|
-
]
|
|
881
|
-
},
|
|
882
|
-
attachmentsMessage,
|
|
883
|
-
{
|
|
884
|
-
id: "k-m2",
|
|
885
|
-
role: "assistant",
|
|
886
|
-
status: "done",
|
|
887
|
-
createdAt: at(5),
|
|
888
|
-
model: "claude-sonnet-5-5",
|
|
889
|
-
parts: [
|
|
890
|
-
{
|
|
891
|
-
type: "text",
|
|
892
|
-
text: "Mockup, notes and spec received. The recording was too large to attach. Here are the current screens and the alert sound:"
|
|
893
|
-
},
|
|
894
|
-
{
|
|
895
|
-
type: "media",
|
|
896
|
-
items: [
|
|
897
|
-
{
|
|
898
|
-
kind: "image",
|
|
899
|
-
src: picture("Forecast card", "#3b63fb", "#7a5af8"),
|
|
900
|
-
alt: "The forecast card in light mode"
|
|
901
|
-
},
|
|
902
|
-
{
|
|
903
|
-
kind: "image",
|
|
904
|
-
src: picture("Dark card", "#1d1d1d", "#3b63fb"),
|
|
905
|
-
alt: "The forecast card in dark mode"
|
|
906
|
-
},
|
|
907
|
-
{
|
|
908
|
-
kind: "image",
|
|
909
|
-
src: picture("Phone", "#0a7a43", "#12b886", 360, 640),
|
|
910
|
-
alt: "The forecast card on a phone"
|
|
911
|
-
},
|
|
912
|
-
{
|
|
913
|
-
kind: "video",
|
|
914
|
-
src: "",
|
|
915
|
-
poster: picture("\u25B6 Walkthrough", "#292929", "#717171"),
|
|
916
|
-
alt: "A walkthrough of the forecast page"
|
|
917
|
-
},
|
|
918
|
-
{ kind: "audio", src: tone(), alt: "The storm alert sound" }
|
|
919
|
-
]
|
|
920
|
-
}
|
|
921
|
-
]
|
|
922
|
-
},
|
|
923
|
-
{ id: "k-u3", role: "user", text: "Show me every tool you used.", createdAt: at(8) },
|
|
924
|
-
{
|
|
925
|
-
id: "k-m3",
|
|
926
|
-
role: "assistant",
|
|
927
|
-
status: "done",
|
|
928
|
-
createdAt: at(9),
|
|
929
|
-
model: "claude-sonnet-5-5",
|
|
930
|
-
parts: [
|
|
931
|
-
{ type: "text", text: "Every tool from today\u2019s run, in order:" },
|
|
932
|
-
...toolShapes.map((call) => ({ type: "tool", tool: call }))
|
|
933
|
-
]
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
id: "k-tool1",
|
|
937
|
-
role: "tool",
|
|
938
|
-
createdAt: at(12),
|
|
939
|
-
tool: tool(
|
|
940
|
-
"k-t15",
|
|
941
|
-
"bash",
|
|
942
|
-
"You ran a command",
|
|
943
|
-
"git log --oneline -3",
|
|
944
|
-
"a1b2c3d fix: expire entries from the previous day\ne4f5a6b feat: retry upstream timeouts\n0c9d8e7 chore: update wrangler"
|
|
945
|
-
)
|
|
946
|
-
}
|
|
947
|
-
];
|
|
948
|
-
}
|
|
949
|
-
function runs() {
|
|
950
|
-
return [
|
|
951
|
-
{
|
|
952
|
-
id: "k-u6",
|
|
953
|
-
role: "user",
|
|
954
|
-
text: "Get the forecast docs ready for the Celsius release.",
|
|
955
|
-
createdAt: at(13)
|
|
956
|
-
},
|
|
957
|
-
{
|
|
958
|
-
id: "k-m8",
|
|
959
|
-
role: "assistant",
|
|
960
|
-
status: "done",
|
|
961
|
-
createdAt: at(13.1),
|
|
962
|
-
model: "claude-sonnet-5-5",
|
|
963
|
-
parts: [
|
|
964
|
-
{ type: "text", text: "Checking what the docs and the API say today." },
|
|
965
|
-
...[
|
|
966
|
-
tool(
|
|
967
|
-
"k-r1",
|
|
968
|
-
"bash",
|
|
969
|
-
"Ask the API for Lisbon",
|
|
970
|
-
"curl -s https://api.example.com/v2/forecast?city=Lisbon",
|
|
971
|
-
'{ "city": "Lisbon", "high": 71, "low": 58, "unit": "F" }'
|
|
972
|
-
),
|
|
973
|
-
tool(
|
|
974
|
-
"k-r2",
|
|
975
|
-
"bash",
|
|
976
|
-
"Read the forecast page",
|
|
977
|
-
"cat /workspace/harbor/docs/forecast.md",
|
|
978
|
-
"# Forecast\n\nTemperatures are in Fahrenheit."
|
|
979
|
-
),
|
|
980
|
-
tool("k-r3", "bash", "See what changed", "git status --short", " M src/lib/units.ts"),
|
|
981
|
-
tool(
|
|
982
|
-
"k-r4",
|
|
983
|
-
"read_file",
|
|
984
|
-
"Read the units helper",
|
|
985
|
-
"/workspace/harbor/src/lib/units.ts",
|
|
986
|
-
"export function toCelsius(f: number): number {\u2026}",
|
|
987
|
-
{ paths: ["/workspace/harbor/src/lib/units.ts"] }
|
|
988
|
-
)
|
|
989
|
-
].map((call) => ({ type: "tool", tool: call })),
|
|
990
|
-
{
|
|
991
|
-
type: "text",
|
|
992
|
-
text: "The page still says Fahrenheit. Updating it, then building and testing."
|
|
993
|
-
},
|
|
994
|
-
...[
|
|
995
|
-
tool(
|
|
996
|
-
"k-r5",
|
|
997
|
-
"edit_file",
|
|
998
|
-
"Switch the page to Celsius",
|
|
999
|
-
"/workspace/harbor/docs/forecast.md",
|
|
1000
|
-
"Edited 1 line",
|
|
1001
|
-
{
|
|
1002
|
-
paths: ["/workspace/harbor/docs/forecast.md"],
|
|
1003
|
-
diff: {
|
|
1004
|
-
before: "# Forecast\n\nTemperatures are in Fahrenheit.\n",
|
|
1005
|
-
after: "# Forecast\n\nTemperatures are in Celsius.\n"
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
),
|
|
1009
|
-
tool("k-r6", "bash", "Build the site", "npm run build", "Built 14 pages in 2.1s", {
|
|
1010
|
-
meta: "timeout 120s"
|
|
1011
|
-
}),
|
|
1012
|
-
tool(
|
|
1013
|
-
"k-r7",
|
|
1014
|
-
"bash",
|
|
1015
|
-
"Run the unit tests",
|
|
1016
|
-
"npx vitest run src/lib/units.test.ts",
|
|
1017
|
-
"FAIL src/lib/units.test.ts > rounds half to even\nExpected 22, received 21",
|
|
1018
|
-
{
|
|
1019
|
-
status: "error",
|
|
1020
|
-
notes: [{ severity: "error", text: "Exited with code 1" }]
|
|
1021
|
-
}
|
|
1022
|
-
),
|
|
1023
|
-
tool(
|
|
1024
|
-
"k-r8",
|
|
1025
|
-
"bash",
|
|
1026
|
-
"Open the release pull request",
|
|
1027
|
-
'gh pr create --repo https://git.example.com/example/harbor --title "Celsius docs"',
|
|
1028
|
-
"Stopped before the tests pass",
|
|
1029
|
-
{ status: "cancelled" }
|
|
1030
|
-
),
|
|
1031
|
-
tool("k-r9", "bash", "Watch the preview logs", "wrangler tail --env preview", "", {
|
|
1032
|
-
status: "running"
|
|
1033
|
-
})
|
|
1034
|
-
].map((call) => ({ type: "tool", tool: call })),
|
|
1035
|
-
{
|
|
1036
|
-
type: "text",
|
|
1037
|
-
text: "One test fails on rounding, so the pull request waits for the fix."
|
|
1038
|
-
}
|
|
1039
|
-
]
|
|
1040
|
-
},
|
|
1041
|
-
{
|
|
1042
|
-
id: "k-tool2",
|
|
1043
|
-
role: "tool",
|
|
1044
|
-
createdAt: at(13.5),
|
|
1045
|
-
tool: tool(
|
|
1046
|
-
"k-r10",
|
|
1047
|
-
"bash",
|
|
1048
|
-
"Check the preview port",
|
|
1049
|
-
"lsof -i :8787",
|
|
1050
|
-
"workerd 4120 harbor 12u IPv4 TCP localhost:8787 (LISTEN)"
|
|
1051
|
-
)
|
|
1052
|
-
}
|
|
1053
|
-
];
|
|
1054
|
-
}
|
|
1055
|
-
function work() {
|
|
1056
|
-
return [
|
|
1057
|
-
{
|
|
1058
|
-
id: "k-m4",
|
|
1059
|
-
role: "assistant",
|
|
1060
|
-
status: "done",
|
|
1061
|
-
createdAt: at(14),
|
|
1062
|
-
model: "claude-sonnet-5-5",
|
|
1063
|
-
parts: [
|
|
1064
|
-
{ type: "text", text: "Here is where things stand." },
|
|
1065
|
-
{ type: "sprinkle", sprinkle: "status" },
|
|
1066
|
-
{
|
|
1067
|
-
type: "delegation",
|
|
1068
|
-
kind: "scoop",
|
|
1069
|
-
scoop: "docs-pass",
|
|
1070
|
-
text: "Started a scoop to update the docs"
|
|
1071
|
-
},
|
|
1072
|
-
{
|
|
1073
|
-
type: "delegation",
|
|
1074
|
-
kind: "feed",
|
|
1075
|
-
scoop: "docs-pass",
|
|
1076
|
-
text: "Handed over the new screenshots"
|
|
1077
|
-
},
|
|
1078
|
-
{
|
|
1079
|
-
type: "delegation",
|
|
1080
|
-
kind: "sprinkle",
|
|
1081
|
-
scoop: "suggestions",
|
|
1082
|
-
text: "Opened the suggestions sprinkle"
|
|
1083
|
-
},
|
|
1084
|
-
{ type: "delegation", kind: "drop", scoop: "amber-heron", text: "Wrapped up amber-heron" }
|
|
1085
|
-
]
|
|
1086
|
-
},
|
|
1087
|
-
{
|
|
1088
|
-
id: "k-m5",
|
|
1089
|
-
role: "assistant",
|
|
1090
|
-
status: "done",
|
|
1091
|
-
createdAt: at(18),
|
|
1092
|
-
model: "claude-sonnet-5-5",
|
|
1093
|
-
parts: [
|
|
1094
|
-
{ type: "text", text: "Three decisions from earlier:" },
|
|
1095
|
-
{
|
|
1096
|
-
type: "question",
|
|
1097
|
-
question: {
|
|
1098
|
-
id: "k-q1",
|
|
1099
|
-
kind: "yes-no",
|
|
1100
|
-
question: "Tag 1.9.0 after the merge?",
|
|
1101
|
-
options: [],
|
|
1102
|
-
state: "answered",
|
|
1103
|
-
answer: "Yes"
|
|
1104
|
-
}
|
|
1105
|
-
},
|
|
1106
|
-
{
|
|
1107
|
-
type: "question",
|
|
1108
|
-
question: {
|
|
1109
|
-
id: "k-q2",
|
|
1110
|
-
kind: "text",
|
|
1111
|
-
question: "What should the release be called?",
|
|
1112
|
-
options: [],
|
|
1113
|
-
state: "inert"
|
|
1114
|
-
}
|
|
1115
|
-
},
|
|
1116
|
-
{
|
|
1117
|
-
type: "question",
|
|
1118
|
-
question: {
|
|
1119
|
-
id: "k-q3",
|
|
1120
|
-
kind: "choice",
|
|
1121
|
-
question: "Where should the cache live?",
|
|
1122
|
-
options: ["Keep it in memory", "Move it to KV", "Decide after the load test"],
|
|
1123
|
-
state: "open"
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
]
|
|
1127
|
-
}
|
|
1128
|
-
];
|
|
1129
|
-
}
|
|
1130
|
-
function events() {
|
|
1131
|
-
return [
|
|
1132
|
-
{
|
|
1133
|
-
id: "k-s1",
|
|
1134
|
-
role: "system",
|
|
1135
|
-
kind: "compaction",
|
|
1136
|
-
title: "Context compacted",
|
|
1137
|
-
text: "58 earlier messages were summarized.",
|
|
1138
|
-
trigger: "threshold",
|
|
1139
|
-
state: "summarized",
|
|
1140
|
-
createdAt: at(1440)
|
|
1141
|
-
},
|
|
1142
|
-
{
|
|
1143
|
-
id: "k-s2",
|
|
1144
|
-
role: "system",
|
|
1145
|
-
kind: "compaction",
|
|
1146
|
-
title: "Compaction failed, transcript truncated",
|
|
1147
|
-
text: "The summary failed, so the oldest 20 messages were dropped instead.",
|
|
1148
|
-
trigger: "overflow",
|
|
1149
|
-
state: "fallback",
|
|
1150
|
-
createdAt: at(1441)
|
|
1151
|
-
},
|
|
1152
|
-
...licks.map(
|
|
1153
|
-
([channel, title, text, body, extra], index2) => ({
|
|
1154
|
-
id: `k-l${index2 + 1}`,
|
|
1155
|
-
role: "lick",
|
|
1156
|
-
channel,
|
|
1157
|
-
title,
|
|
1158
|
-
text,
|
|
1159
|
-
createdAt: at(1445 + index2),
|
|
1160
|
-
...body ? { body } : {},
|
|
1161
|
-
...extra
|
|
1162
|
-
})
|
|
1163
|
-
),
|
|
1164
|
-
{
|
|
1165
|
-
id: "k-u4",
|
|
1166
|
-
role: "user",
|
|
1167
|
-
text: "Actually, stop the deploy, staging is frozen today.",
|
|
1168
|
-
mode: "steer",
|
|
1169
|
-
createdAt: at(1470)
|
|
1170
|
-
},
|
|
1171
|
-
{
|
|
1172
|
-
id: "k-m6",
|
|
1173
|
-
role: "assistant",
|
|
1174
|
-
status: "stopped",
|
|
1175
|
-
createdAt: at(1470),
|
|
1176
|
-
model: "claude-haiku-4-5",
|
|
1177
|
-
parts: [{ type: "text", text: "Stopping the staging deploy and leaving" }]
|
|
1178
|
-
},
|
|
1179
|
-
{
|
|
1180
|
-
id: "k-u5",
|
|
1181
|
-
role: "user",
|
|
1182
|
-
text: "Update the forecast page to Celsius and use the new screenshots.",
|
|
1183
|
-
from: "harbor",
|
|
1184
|
-
createdAt: at(1472)
|
|
1185
|
-
},
|
|
1186
|
-
{
|
|
1187
|
-
id: "k-m7",
|
|
1188
|
-
role: "assistant",
|
|
1189
|
-
status: "error",
|
|
1190
|
-
createdAt: at(1475),
|
|
1191
|
-
model: "claude-haiku-4-5",
|
|
1192
|
-
parts: [
|
|
1193
|
-
{
|
|
1194
|
-
type: "thinking",
|
|
1195
|
-
text: "The model rejected the request because the context is too large."
|
|
1196
|
-
},
|
|
1197
|
-
{
|
|
1198
|
-
type: "error",
|
|
1199
|
-
message: "The request is too large for this model.",
|
|
1200
|
-
detail: "The model returned 413: the request is larger than its context window.",
|
|
1201
|
-
action: "change-model"
|
|
1202
|
-
}
|
|
1203
|
-
]
|
|
1204
|
-
},
|
|
1205
|
-
{
|
|
1206
|
-
id: "k-s3",
|
|
1207
|
-
role: "system",
|
|
1208
|
-
kind: "error",
|
|
1209
|
-
title: "docs-pass failed",
|
|
1210
|
-
text: "The Adobe session expired. Log in again to continue.",
|
|
1211
|
-
action: "login",
|
|
1212
|
-
account: "ai-adobe",
|
|
1213
|
-
createdAt: at(1476)
|
|
1214
|
-
},
|
|
1215
|
-
{
|
|
1216
|
-
id: "k-s4",
|
|
1217
|
-
role: "system",
|
|
1218
|
-
kind: "error",
|
|
1219
|
-
title: "Rate limited",
|
|
1220
|
-
text: "Anthropic answered 429. Retrying in 30 seconds.",
|
|
1221
|
-
action: "retry",
|
|
1222
|
-
createdAt: at(1477)
|
|
1223
|
-
},
|
|
1224
|
-
{
|
|
1225
|
-
id: "k-s5",
|
|
1226
|
-
role: "system",
|
|
1227
|
-
kind: "notice",
|
|
1228
|
-
title: "Model changed",
|
|
1229
|
-
text: "to Claude Opus 5.5 (Anthropic)",
|
|
1230
|
-
createdAt: at(1478)
|
|
1231
|
-
}
|
|
1232
|
-
];
|
|
1233
|
-
}
|
|
1234
|
-
function showcase() {
|
|
1235
|
-
return [...opening(), ...runs(), ...work(), ...events()];
|
|
1236
|
-
}
|
|
1237
|
-
function showcaseQueue() {
|
|
1238
|
-
return [
|
|
1239
|
-
{
|
|
1240
|
-
id: "k-q-1",
|
|
1241
|
-
role: "user",
|
|
1242
|
-
text: "Then open a pull request for the docs.",
|
|
1243
|
-
createdAt: at(1480),
|
|
1244
|
-
queued: true,
|
|
1245
|
-
mode: "queue"
|
|
1246
|
-
},
|
|
1247
|
-
{
|
|
1248
|
-
id: "k-q-2",
|
|
1249
|
-
role: "user",
|
|
1250
|
-
text: "And post the link in the release channel.",
|
|
1251
|
-
createdAt: at(1481),
|
|
1252
|
-
queued: true,
|
|
1253
|
-
mode: "queue"
|
|
1254
|
-
}
|
|
1255
|
-
];
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
// src/dummy/fixtures.ts
|
|
1259
|
-
var cacheBefore = `export interface Entry<T> {
|
|
1260
|
-
value: T;
|
|
1261
|
-
storedAt: number;
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
const TTL = 60 * 60 * 1000;
|
|
1265
|
-
|
|
1266
|
-
export class ForecastCache<T> {
|
|
1267
|
-
#entries = new Map<string, Entry<T>>();
|
|
1268
|
-
|
|
1269
|
-
get(key: string, now = Date.now()): T | undefined {
|
|
1270
|
-
const entry = this.#entries.get(key);
|
|
1271
|
-
if (!entry) return undefined;
|
|
1272
|
-
const day = new Date(entry.storedAt).getDate();
|
|
1273
|
-
if (day !== new Date(now).getDate()) return entry.value;
|
|
1274
|
-
if (now - entry.storedAt > TTL) return undefined;
|
|
1275
|
-
return entry.value;
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
set(key: string, value: T, now = Date.now()): void {
|
|
1279
|
-
this.#entries.set(key, { value, storedAt: now });
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
`;
|
|
1283
|
-
var cacheAfter = `export interface Entry<T> {
|
|
1284
|
-
value: T;
|
|
1285
|
-
storedAt: number;
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
const TTL = 60 * 60 * 1000;
|
|
1289
|
-
|
|
1290
|
-
export class ForecastCache<T> {
|
|
1291
|
-
#entries = new Map<string, Entry<T>>();
|
|
1292
|
-
|
|
1293
|
-
get(key: string, now = Date.now()): T | undefined {
|
|
1294
|
-
const entry = this.#entries.get(key);
|
|
1295
|
-
if (!entry) return undefined;
|
|
1296
|
-
if (dayOf(entry.storedAt) !== dayOf(now)) {
|
|
1297
|
-
this.#entries.delete(key);
|
|
1298
|
-
return undefined;
|
|
1299
|
-
}
|
|
1300
|
-
if (now - entry.storedAt > TTL) return undefined;
|
|
1301
|
-
return entry.value;
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
set(key: string, value: T, now = Date.now()): void {
|
|
1305
|
-
this.#entries.set(key, { value, storedAt: now });
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
function dayOf(time: number): string {
|
|
1310
|
-
return new Date(time).toISOString().slice(0, 10);
|
|
1311
|
-
}
|
|
1312
|
-
`;
|
|
1313
|
-
var retry = `export async function retry<T>(task: () => Promise<T>, attempts = 3, wait = 250): Promise<T> {
|
|
1314
|
-
let last: unknown;
|
|
1315
|
-
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
1316
|
-
try {
|
|
1317
|
-
return await task();
|
|
1318
|
-
} catch (error) {
|
|
1319
|
-
last = error;
|
|
1320
|
-
await new Promise((resolve) => setTimeout(resolve, wait * 2 ** attempt));
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
throw last;
|
|
1324
|
-
}
|
|
1325
|
-
`;
|
|
1326
|
-
var legacyXml = `export function toXml(forecast: { city: string; high: number; low: number }): string {
|
|
1327
|
-
return \`<forecast city="\${forecast.city}"><high>\${forecast.high}</high><low>\${forecast.low}</low></forecast>\`;
|
|
1328
|
-
}
|
|
1329
|
-
`;
|
|
1330
|
-
var forecastBefore = `import { ForecastCache } from '../lib/cache.ts';
|
|
1331
|
-
import { toCelsius } from '../lib/units.ts';
|
|
1332
|
-
|
|
1333
|
-
const cache = new ForecastCache<Forecast>();
|
|
1334
|
-
|
|
1335
|
-
export interface Forecast {
|
|
1336
|
-
city: string;
|
|
1337
|
-
high: number;
|
|
1338
|
-
low: number;
|
|
1339
|
-
summary: string;
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
export async function forecast(city: string, upstream: typeof fetch = fetch): Promise<Forecast> {
|
|
1343
|
-
const cached = cache.get(city);
|
|
1344
|
-
if (cached) return cached;
|
|
1345
|
-
const response = await upstream(\`https://api.example.com/v2/forecast?city=\${encodeURIComponent(city)}\`);
|
|
1346
|
-
const body = await response.json();
|
|
1347
|
-
const result = {
|
|
1348
|
-
city,
|
|
1349
|
-
high: toCelsius(body.daily.max),
|
|
1350
|
-
low: toCelsius(body.daily.min),
|
|
1351
|
-
summary: body.daily.summary,
|
|
1352
|
-
};
|
|
1353
|
-
cache.set(city, result);
|
|
1354
|
-
return result;
|
|
1355
|
-
}
|
|
1356
|
-
`;
|
|
1357
|
-
var forecastAfter = forecastBefore.replace(
|
|
1358
|
-
"import { toCelsius } from '../lib/units.ts';",
|
|
1359
|
-
"import { retry } from '../lib/retry.ts';\nimport { toCelsius } from '../lib/units.ts';"
|
|
1360
|
-
).replace("const response = await upstream(", "const response = await retry(() =>\n upstream(").replace("(city)}`);", "(city)}`)\n );");
|
|
1361
|
-
var units = `export function toCelsius(fahrenheit: number): number {
|
|
1362
|
-
return Math.round(((fahrenheit - 32) * 5) / 9);
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
export function toFahrenheit(celsius: number): number {
|
|
1366
|
-
return Math.round((celsius * 9) / 5 + 32);
|
|
1367
|
-
}
|
|
1368
|
-
`;
|
|
1369
|
-
var unitsTest = `import assert from 'node:assert/strict';
|
|
1370
|
-
import { test } from 'node:test';
|
|
1371
|
-
import { toCelsius, toFahrenheit } from '../src/lib/units.ts';
|
|
1372
|
-
|
|
1373
|
-
test('converts both ways', () => {
|
|
1374
|
-
assert.equal(toCelsius(212), 100);
|
|
1375
|
-
assert.equal(toFahrenheit(0), 32);
|
|
1376
|
-
});
|
|
1377
|
-
`;
|
|
1378
|
-
var index = `import { forecast } from './routes/forecast.ts';
|
|
1379
|
-
|
|
1380
|
-
export default {
|
|
1381
|
-
async fetch(request: Request): Promise<Response> {
|
|
1382
|
-
const url = new URL(request.url);
|
|
1383
|
-
if (url.pathname === '/health') return new Response('ok');
|
|
1384
|
-
const city = url.searchParams.get('city');
|
|
1385
|
-
if (!city) return Response.json({ error: 'city is required' }, { status: 400 });
|
|
1386
|
-
return Response.json(await forecast(city));
|
|
1387
|
-
},
|
|
1388
|
-
};
|
|
1389
|
-
`;
|
|
1390
|
-
var readme = `# harbor
|
|
1391
|
-
|
|
1392
|
-
A small forecast API that sits in front of the upstream weather service and caches per city.
|
|
1393
|
-
|
|
1394
|
-
## Development
|
|
1395
|
-
|
|
1396
|
-
\`\`\`sh
|
|
1397
|
-
npm install
|
|
1398
|
-
npm test
|
|
1399
|
-
npm run dev
|
|
1400
|
-
\`\`\`
|
|
1401
|
-
|
|
1402
|
-
The worker listens on \`http://localhost:8787\`. Try \`/forecast?city=Lisbon\`.
|
|
1403
|
-
`;
|
|
1404
|
-
var pkg = `{
|
|
1405
|
-
"name": "harbor",
|
|
1406
|
-
"private": true,
|
|
1407
|
-
"type": "module",
|
|
1408
|
-
"scripts": {
|
|
1409
|
-
"dev": "wrangler dev",
|
|
1410
|
-
"test": "node --test test/"
|
|
1411
|
-
},
|
|
1412
|
-
"devDependencies": {
|
|
1413
|
-
"wrangler": "4.40.0"
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
`;
|
|
1417
|
-
var skill = `---
|
|
1418
|
-
name: release-notes
|
|
1419
|
-
description: Draft release notes from merged pull requests since the last tag.
|
|
1420
|
-
---
|
|
1421
|
-
|
|
1422
|
-
1. Run \`collect.jsh\` with the repository and the previous tag.
|
|
1423
|
-
2. Group the pull requests by label: features, fixes, chores.
|
|
1424
|
-
3. Write one sentence per change, in the past tense, without ticket numbers.
|
|
1425
|
-
`;
|
|
1426
|
-
var collect = `const [repo, since] = process.argv.slice(2);
|
|
1427
|
-
const pulls = await gh(\`repos/\${repo}/pulls?state=closed&per_page=100\`);
|
|
1428
|
-
for (const pull of pulls.filter((p) => p.merged_at > since)) {
|
|
1429
|
-
console.log(\`\${pull.number}\\t\${pull.labels.map((l) => l.name).join(',')}\\t\${pull.title}\`);
|
|
1430
|
-
}
|
|
1431
|
-
`;
|
|
1432
|
-
var memory = `# Shared memory
|
|
1433
|
-
|
|
1434
|
-
## Preferences
|
|
1435
|
-
- Keep replies short. Lead with the result, then the evidence.
|
|
1436
|
-
- Run the tests before saying something is fixed.
|
|
1437
|
-
|
|
1438
|
-
## Projects
|
|
1439
|
-
- harbor: forecast API, deployed as a worker. Cache bugs show up around midnight UTC.
|
|
1440
|
-
- release-notes: drafted every Friday from merged pull requests.
|
|
1441
|
-
`;
|
|
1442
|
-
var standup = `# Standup, 4 October
|
|
1443
|
-
|
|
1444
|
-
- harbor: retries for upstream timeouts are in review.
|
|
1445
|
-
- Docs: the forecast endpoint page still shows Fahrenheit examples.
|
|
1446
|
-
- Next: move the cache to KV once the TTL fix lands.
|
|
1447
|
-
`;
|
|
1448
|
-
var sample = `{
|
|
1449
|
-
"city": "Lisbon",
|
|
1450
|
-
"daily": { "max": 77, "min": 61, "summary": "Clear, light wind from the north" }
|
|
1451
|
-
}
|
|
1452
|
-
`;
|
|
1453
|
-
var bashrc = `export PS1='\\u@slicc:\\w\\$ '
|
|
1454
|
-
alias ll='ls -la'
|
|
1455
|
-
`;
|
|
1456
|
-
var suggestions = `${JSON.stringify(
|
|
1457
|
-
[
|
|
1458
|
-
{
|
|
1459
|
-
id: "sug-units",
|
|
1460
|
-
kind: "skill",
|
|
1461
|
-
title: "Unit conversions",
|
|
1462
|
-
body: "You converted Fahrenheit to Celsius by hand in three chats this week. A skill can do it for you.",
|
|
1463
|
-
skill: "unit-converter",
|
|
1464
|
-
install: "unit-converter from raw.githubusercontent.com/example/skills/main/unit-converter/SKILL.md"
|
|
1465
|
-
},
|
|
1466
|
-
{
|
|
1467
|
-
id: "sug-notes",
|
|
1468
|
-
kind: "use-case",
|
|
1469
|
-
title: "Release notes from the week",
|
|
1470
|
-
body: "You write release notes for harbor every Friday. Let the cone draft them from the commits.",
|
|
1471
|
-
prompt: "Draft release notes for harbor from the commits since last Friday."
|
|
1472
|
-
},
|
|
1473
|
-
{
|
|
1474
|
-
id: "sug-cache",
|
|
1475
|
-
kind: "tip",
|
|
1476
|
-
title: "Name your cache keys",
|
|
1477
|
-
body: "Forecast cache keys mix the city and the units. Splitting them makes stale entries easier to find."
|
|
1478
|
-
}
|
|
1479
|
-
],
|
|
1480
|
-
null,
|
|
1481
|
-
2
|
|
1482
|
-
)}
|
|
1483
|
-
`;
|
|
1484
|
-
var files = {
|
|
1485
|
-
"/workspace/harbor/README.md": readme,
|
|
1486
|
-
"/workspace/harbor/package.json": pkg,
|
|
1487
|
-
"/workspace/harbor/wrangler.toml": 'name = "harbor"\nmain = "src/index.ts"\ncompatibility_date = "2026-09-01"\n',
|
|
1488
|
-
"/workspace/harbor/.gitignore": "node_modules/\n.wrangler/\n",
|
|
1489
|
-
"/workspace/harbor/src/index.ts": index,
|
|
1490
|
-
"/workspace/harbor/src/routes/forecast.ts": forecastBefore,
|
|
1491
|
-
"/workspace/harbor/src/lib/cache.ts": cacheBefore,
|
|
1492
|
-
"/workspace/harbor/src/lib/units.ts": units,
|
|
1493
|
-
"/workspace/harbor/src/legacy/xml.ts": legacyXml,
|
|
1494
|
-
"/workspace/harbor/static/radar.png": "\x89PNG\r\n\n",
|
|
1495
|
-
"/workspace/harbor/test/units.test.ts": unitsTest,
|
|
1496
|
-
"/workspace/skills/release-notes/SKILL.md": skill,
|
|
1497
|
-
"/workspace/skills/release-notes/scripts/collect.jsh": collect,
|
|
1498
|
-
"/shared/MEMORY.md": memory,
|
|
1499
|
-
"/shared/notes/2026-10-04-standup.md": standup,
|
|
1500
|
-
"/home/user/.bashrc": bashrc,
|
|
1501
|
-
"/home/.gelatiere/suggestions.json": suggestions,
|
|
1502
|
-
"/tmp/forecast-sample.json": sample
|
|
1503
|
-
};
|
|
1504
|
-
var directories = ["/sessions", "/home/user/downloads"];
|
|
1505
|
-
var mountPoint = "/mnt/photos";
|
|
1506
|
-
var mounted = {
|
|
1507
|
-
"README.md": "# Photos\n\nHarbor shots for the forecast pages, one album per season.\n",
|
|
1508
|
-
"albums/autumn.json": '{ "title": "Autumn", "shots": ["breakwater-fog", "pier-at-dusk"] }\n',
|
|
1509
|
-
"albums/winter.json": '{ "title": "Winter", "shots": ["ice-on-the-buoys"] }\n',
|
|
1510
|
-
"captions.txt": "breakwater-fog: low cloud over the outer wall\npier-at-dusk: lamps on, tide out\n"
|
|
1511
|
-
};
|
|
1512
|
-
var pending = [
|
|
1513
|
-
{
|
|
1514
|
-
path: "/workspace/harbor/src/lib/cache.ts",
|
|
1515
|
-
status: "modified",
|
|
1516
|
-
before: cacheBefore,
|
|
1517
|
-
after: cacheAfter,
|
|
1518
|
-
agentId: "cone-harbor"
|
|
1519
|
-
},
|
|
1520
|
-
{
|
|
1521
|
-
path: "/workspace/harbor/src/lib/retry.ts",
|
|
1522
|
-
status: "added",
|
|
1523
|
-
before: null,
|
|
1524
|
-
after: retry,
|
|
1525
|
-
agentId: "scoop-otter"
|
|
1526
|
-
},
|
|
1527
|
-
{
|
|
1528
|
-
path: "/workspace/harbor/src/routes/forecast.ts",
|
|
1529
|
-
status: "modified",
|
|
1530
|
-
before: forecastBefore,
|
|
1531
|
-
after: forecastAfter,
|
|
1532
|
-
agentId: "scoop-otter"
|
|
1533
|
-
},
|
|
1534
|
-
{
|
|
1535
|
-
path: "/workspace/harbor/src/legacy/xml.ts",
|
|
1536
|
-
status: "deleted",
|
|
1537
|
-
before: legacyXml,
|
|
1538
|
-
after: null,
|
|
1539
|
-
agentId: "cone-harbor"
|
|
1540
|
-
},
|
|
1541
|
-
{
|
|
1542
|
-
path: "/workspace/harbor/static/radar.png",
|
|
1543
|
-
status: "modified",
|
|
1544
|
-
before: null,
|
|
1545
|
-
after: null,
|
|
1546
|
-
agentId: "scoop-otter"
|
|
1547
|
-
}
|
|
1548
|
-
];
|
|
1549
|
-
var skillEdit = {
|
|
1550
|
-
path: "/workspace/skills/release-notes/SKILL.md",
|
|
1551
|
-
status: "modified",
|
|
1552
|
-
before: skill,
|
|
1553
|
-
after: `${skill}4. Link each change to its pull request.
|
|
1554
|
-
`,
|
|
1555
|
-
agentId: null
|
|
1556
|
-
};
|
|
1557
|
-
var models = [
|
|
1558
|
-
{ id: "claude-opus-5-5", label: "Claude Opus 5.5", provider: "Anthropic", images: true },
|
|
1559
|
-
{ id: "claude-sonnet-5-5", label: "Claude Sonnet 5.5", provider: "Anthropic", images: true },
|
|
1560
|
-
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5", provider: "Anthropic", images: true },
|
|
1561
|
-
{ id: "local-small", label: "Local (ort-llama)", provider: "This browser", images: false },
|
|
1562
|
-
{ id: "local-guard", label: "Local guard", provider: "This browser", kind: "classifier" },
|
|
1563
|
-
{ id: "local-tiny", label: "Local tiny (no tools)", provider: "This browser", tools: false }
|
|
1564
|
-
];
|
|
1565
|
-
var laterModels = [
|
|
1566
|
-
{ id: "gpt-6", label: "GPT-6", provider: "OpenAI", images: true },
|
|
1567
|
-
{ id: "gpt-6-mini", label: "GPT-6 mini", provider: "OpenAI", images: true, default: true }
|
|
1568
|
-
];
|
|
1569
|
-
var defaults = {
|
|
1570
|
-
color: "system",
|
|
1571
|
-
model: "claude-sonnet-5-5",
|
|
1572
|
-
thinking: "medium",
|
|
1573
|
-
sendOnEnter: true,
|
|
1574
|
-
pushToTalk: false,
|
|
1575
|
-
showThinking: true,
|
|
1576
|
-
diffStyle: "unified"
|
|
1577
|
-
};
|
|
1578
|
-
var accounts = [
|
|
1579
|
-
{ id: "anthropic", provider: "Anthropic", identity: "API key ending 3f2a", status: "connected" },
|
|
1580
|
-
{ id: "github", provider: "GitHub", identity: "@example-dev", status: "connected" },
|
|
1581
|
-
{ id: "adobe", provider: "Adobe", identity: "dev@example.com", status: "expired" },
|
|
1582
|
-
{
|
|
1583
|
-
id: "ai-adobe",
|
|
1584
|
-
provider: "Adobe AI",
|
|
1585
|
-
identity: "harbor-ops@example.com",
|
|
1586
|
-
status: "expired",
|
|
1587
|
-
auth: "oauth"
|
|
1588
|
-
},
|
|
1589
|
-
{ id: "openai", provider: "OpenAI", identity: "", status: "disconnected", auth: "api-key" },
|
|
1590
|
-
{
|
|
1591
|
-
id: "amazon-bedrock",
|
|
1592
|
-
provider: "Amazon Bedrock",
|
|
1593
|
-
identity: "",
|
|
1594
|
-
status: "disconnected",
|
|
1595
|
-
auth: "api-key",
|
|
1596
|
-
fields: [
|
|
1597
|
-
{
|
|
1598
|
-
name: "region",
|
|
1599
|
-
label: "Region",
|
|
1600
|
-
required: true,
|
|
1601
|
-
options: [
|
|
1602
|
-
{ value: "us-east-1", label: "US East (N. Virginia)" },
|
|
1603
|
-
{ value: "us-west-2", label: "US West (Oregon)" },
|
|
1604
|
-
{ value: "eu-central-1", label: "Europe (Frankfurt)" },
|
|
1605
|
-
{ value: "ap-northeast-1", label: "Asia Pacific (Tokyo)" }
|
|
1606
|
-
],
|
|
1607
|
-
value: "us-west-2"
|
|
1608
|
-
}
|
|
1609
|
-
]
|
|
1610
|
-
},
|
|
1611
|
-
{
|
|
1612
|
-
id: "mcp:docs",
|
|
1613
|
-
provider: "docs",
|
|
1614
|
-
identity: "docs.example",
|
|
1615
|
-
status: "connected",
|
|
1616
|
-
auth: "api-key",
|
|
1617
|
-
group: "mcp",
|
|
1618
|
-
secret: "token"
|
|
1619
|
-
},
|
|
1620
|
-
{
|
|
1621
|
-
id: "mcp:tickets",
|
|
1622
|
-
provider: "tickets",
|
|
1623
|
-
identity: "tickets.example",
|
|
1624
|
-
status: "expired",
|
|
1625
|
-
auth: "api-key",
|
|
1626
|
-
group: "mcp",
|
|
1627
|
-
secret: "token"
|
|
1628
|
-
},
|
|
1629
|
-
{
|
|
1630
|
-
id: "mcp:design",
|
|
1631
|
-
provider: "design",
|
|
1632
|
-
identity: "design.example",
|
|
1633
|
-
status: "disconnected",
|
|
1634
|
-
auth: "oauth",
|
|
1635
|
-
group: "mcp"
|
|
1636
|
-
}
|
|
1637
|
-
];
|
|
1638
|
-
var agents = [
|
|
1639
|
-
{
|
|
1640
|
-
id: "cone-sliccy",
|
|
1641
|
-
name: "sliccy",
|
|
1642
|
-
kind: "cone",
|
|
1643
|
-
parentId: null,
|
|
1644
|
-
status: "idle",
|
|
1645
|
-
model: "claude-sonnet-5-5",
|
|
1646
|
-
contextFill: 0.21,
|
|
1647
|
-
unread: 0
|
|
1648
|
-
},
|
|
1649
|
-
showcaseAgent,
|
|
1650
|
-
{
|
|
1651
|
-
id: "cone-harbor",
|
|
1652
|
-
name: "harbor",
|
|
1653
|
-
kind: "cone",
|
|
1654
|
-
parentId: null,
|
|
1655
|
-
status: "idle",
|
|
1656
|
-
model: "claude-opus-5-5",
|
|
1657
|
-
contextFill: 0.38,
|
|
1658
|
-
unread: 0
|
|
1659
|
-
},
|
|
1660
|
-
{
|
|
1661
|
-
id: "scoop-otter",
|
|
1662
|
-
name: "quiet-otter",
|
|
1663
|
-
kind: "scoop",
|
|
1664
|
-
parentId: "cone-harbor",
|
|
1665
|
-
status: "working",
|
|
1666
|
-
model: "claude-haiku-4-5",
|
|
1667
|
-
contextFill: 0.12,
|
|
1668
|
-
unread: 0
|
|
1669
|
-
},
|
|
1670
|
-
{
|
|
1671
|
-
id: "scoop-heron",
|
|
1672
|
-
name: "amber-heron",
|
|
1673
|
-
kind: "scoop",
|
|
1674
|
-
parentId: "cone-harbor",
|
|
1675
|
-
status: "idle",
|
|
1676
|
-
model: "claude-haiku-4-5",
|
|
1677
|
-
contextFill: 0.04,
|
|
1678
|
-
unread: 0
|
|
1679
|
-
},
|
|
1680
|
-
{
|
|
1681
|
-
id: "cone-release",
|
|
1682
|
-
name: "release-notes",
|
|
1683
|
-
kind: "cone",
|
|
1684
|
-
parentId: null,
|
|
1685
|
-
status: "idle",
|
|
1686
|
-
model: "claude-sonnet-5-5",
|
|
1687
|
-
contextFill: 0.7,
|
|
1688
|
-
unread: 1
|
|
1689
|
-
},
|
|
1690
|
-
{
|
|
1691
|
-
id: "cone-triage",
|
|
1692
|
-
name: "inbox-triage",
|
|
1693
|
-
kind: "cone",
|
|
1694
|
-
parentId: null,
|
|
1695
|
-
status: "waiting",
|
|
1696
|
-
model: "claude-sonnet-5-5",
|
|
1697
|
-
contextFill: 0.95,
|
|
1698
|
-
unread: 2
|
|
1699
|
-
},
|
|
1700
|
-
{
|
|
1701
|
-
id: "scoop-wren",
|
|
1702
|
-
name: "tidal-wren",
|
|
1703
|
-
kind: "scoop",
|
|
1704
|
-
parentId: "cone-sliccy",
|
|
1705
|
-
status: "idle",
|
|
1706
|
-
model: "claude-haiku-4-5",
|
|
1707
|
-
contextFill: 0.02,
|
|
1708
|
-
unread: 0
|
|
1709
|
-
},
|
|
1710
|
-
{
|
|
1711
|
-
id: "cone-tides",
|
|
1712
|
-
name: "tide-tables",
|
|
1713
|
-
kind: "cone",
|
|
1714
|
-
parentId: null,
|
|
1715
|
-
status: "idle",
|
|
1716
|
-
model: "claude-sonnet-5-5",
|
|
1717
|
-
contextFill: 0.31,
|
|
1718
|
-
unread: 0,
|
|
1719
|
-
frozen: true
|
|
1720
|
-
}
|
|
1721
|
-
];
|
|
1722
|
-
var start2 = Date.UTC(2026, 9, 5, 8, 30);
|
|
1723
|
-
var at2 = (minutes2) => start2 + minutes2 * 6e4;
|
|
1724
|
-
var queues = { "cone-kitchen": showcaseQueue() };
|
|
1725
|
-
var conversations = {
|
|
1726
|
-
"cone-kitchen": showcase(),
|
|
1727
|
-
"cone-sliccy": [
|
|
1728
|
-
{
|
|
1729
|
-
id: "m-s0",
|
|
1730
|
-
role: "assistant",
|
|
1731
|
-
status: "done",
|
|
1732
|
-
createdAt: at2(-24 * 60),
|
|
1733
|
-
parts: [
|
|
1734
|
-
{
|
|
1735
|
-
type: "text",
|
|
1736
|
-
text: "Hi, I am sliccy. Tell me a little about yourself, and I will set things up for you."
|
|
1737
|
-
},
|
|
1738
|
-
{ type: "sprinkle", sprinkle: "welcome" }
|
|
1739
|
-
]
|
|
1740
|
-
},
|
|
1741
|
-
{
|
|
1742
|
-
id: "m-s1",
|
|
1743
|
-
role: "user",
|
|
1744
|
-
text: "What is on my plate today?",
|
|
1745
|
-
createdAt: at2(0)
|
|
1746
|
-
},
|
|
1747
|
-
{
|
|
1748
|
-
id: "m-s2",
|
|
1749
|
-
role: "assistant",
|
|
1750
|
-
status: "done",
|
|
1751
|
-
createdAt: at2(1),
|
|
1752
|
-
parts: [
|
|
1753
|
-
{
|
|
1754
|
-
type: "tool",
|
|
1755
|
-
tool: {
|
|
1756
|
-
id: "t-s1",
|
|
1757
|
-
name: "read_file",
|
|
1758
|
-
title: "Read the standup notes",
|
|
1759
|
-
input: "/shared/notes/2026-10-04-standup.md",
|
|
1760
|
-
output: standup,
|
|
1761
|
-
status: "done",
|
|
1762
|
-
paths: ["/shared/notes/2026-10-04-standup.md"]
|
|
1763
|
-
}
|
|
1764
|
-
},
|
|
1765
|
-
{
|
|
1766
|
-
type: "text",
|
|
1767
|
-
text: "Three things carry over from yesterday:\n\n1. **harbor**: the retry change is in review, and the midnight cache bug is still open.\n2. **Docs**: the forecast page still shows Fahrenheit examples.\n3. **release-notes** has a draft waiting for you.\n\nThe `harbor` cone is the one to start with."
|
|
1768
|
-
}
|
|
1769
|
-
]
|
|
1770
|
-
}
|
|
1771
|
-
],
|
|
1772
|
-
"cone-harbor": [
|
|
1773
|
-
{
|
|
1774
|
-
id: "m-h1",
|
|
1775
|
-
role: "lick",
|
|
1776
|
-
channel: "webhook",
|
|
1777
|
-
title: "GitHub \xB7 issue opened",
|
|
1778
|
-
text: "example/harbor#57: Forecast stays on yesterday after midnight UTC",
|
|
1779
|
-
createdAt: at2(2)
|
|
1780
|
-
},
|
|
1781
|
-
{
|
|
1782
|
-
id: "m-h2",
|
|
1783
|
-
role: "user",
|
|
1784
|
-
text: "The forecast endpoint serves stale data after midnight UTC. Find out why and fix it.",
|
|
1785
|
-
createdAt: at2(3)
|
|
1786
|
-
},
|
|
1787
|
-
{
|
|
1788
|
-
id: "m-h3",
|
|
1789
|
-
role: "assistant",
|
|
1790
|
-
status: "done",
|
|
1791
|
-
createdAt: at2(4),
|
|
1792
|
-
parts: [
|
|
1793
|
-
{
|
|
1794
|
-
type: "thinking",
|
|
1795
|
-
text: "Stale after midnight points at the cache expiry. Check how the cache compares days."
|
|
1796
|
-
},
|
|
1797
|
-
{
|
|
1798
|
-
type: "tool",
|
|
1799
|
-
tool: {
|
|
1800
|
-
id: "t-h1",
|
|
1801
|
-
name: "bash",
|
|
1802
|
-
title: "Search for the cache expiry",
|
|
1803
|
-
input: 'rg -n "getDate|TTL" src',
|
|
1804
|
-
output: "src/lib/cache.ts:6:const TTL = 60 * 60 * 1000;\nsrc/lib/cache.ts:14: const day = new Date(entry.storedAt).getDate();\nsrc/lib/cache.ts:16: if (now - entry.storedAt > TTL) return undefined;",
|
|
1805
|
-
status: "done",
|
|
1806
|
-
paths: ["/workspace/harbor/src/lib/cache.ts"]
|
|
1807
|
-
}
|
|
1808
|
-
},
|
|
1809
|
-
{
|
|
1810
|
-
type: "tool",
|
|
1811
|
-
tool: {
|
|
1812
|
-
id: "t-h2",
|
|
1813
|
-
name: "read_file",
|
|
1814
|
-
title: "Read the cache",
|
|
1815
|
-
input: "/workspace/harbor/src/lib/cache.ts",
|
|
1816
|
-
output: cacheBefore,
|
|
1817
|
-
status: "done",
|
|
1818
|
-
paths: ["/workspace/harbor/src/lib/cache.ts"]
|
|
1819
|
-
}
|
|
1820
|
-
},
|
|
1821
|
-
{
|
|
1822
|
-
type: "tool",
|
|
1823
|
-
tool: {
|
|
1824
|
-
id: "t-h3",
|
|
1825
|
-
name: "edit_file",
|
|
1826
|
-
title: "Expire entries from the previous day",
|
|
1827
|
-
input: "/workspace/harbor/src/lib/cache.ts",
|
|
1828
|
-
output: "Replaced 1 block (4 lines \u2192 7 lines), added dayOf()",
|
|
1829
|
-
status: "done",
|
|
1830
|
-
paths: ["/workspace/harbor/src/lib/cache.ts"],
|
|
1831
|
-
diff: { before: cacheBefore, after: cacheAfter }
|
|
1832
|
-
}
|
|
1833
|
-
},
|
|
1834
|
-
{
|
|
1835
|
-
type: "tool",
|
|
1836
|
-
tool: {
|
|
1837
|
-
id: "t-h4",
|
|
1838
|
-
name: "bash",
|
|
1839
|
-
title: "Run the tests",
|
|
1840
|
-
input: "npm test",
|
|
1841
|
-
meta: "timeout 120s",
|
|
1842
|
-
output: "> harbor@0.0.0 test\n> node --test test/\n\n\u2714 converts both ways (0.6ms)\n\u2714 expires entries from the previous day (0.4ms)\n\u2139 tests 2\n\u2139 pass 2\n\u2139 fail 0",
|
|
1843
|
-
status: "done",
|
|
1844
|
-
paths: []
|
|
1845
|
-
}
|
|
1846
|
-
},
|
|
1847
|
-
{
|
|
1848
|
-
type: "text",
|
|
1849
|
-
text: "Found it. `ForecastCache.get` compared **local** calendar days, and when the day changed it returned the old entry instead of dropping it:\n\n```ts\nif (day !== new Date(now).getDate()) return entry.value;\n```\n\nI changed it to compare UTC dates and delete the entry, so the first request after midnight goes upstream. The tests pass.\n\n| | Before | After |\n| --- | --- | --- |\n| Day compared | local `getDate()` | UTC `toISOString()` |\n| On a new day | returns stale entry | deletes and refetches |\n\nquiet-otter is adding retries for upstream timeouts in a scoop."
|
|
1850
|
-
},
|
|
1851
|
-
{
|
|
1852
|
-
type: "delegation",
|
|
1853
|
-
kind: "scoop",
|
|
1854
|
-
scoop: "quiet-otter",
|
|
1855
|
-
text: "Started a scoop to add retries around the upstream fetch"
|
|
1856
|
-
}
|
|
1857
|
-
]
|
|
1858
|
-
},
|
|
1859
|
-
{
|
|
1860
|
-
id: "m-h4",
|
|
1861
|
-
role: "lick",
|
|
1862
|
-
channel: "scoop-notify",
|
|
1863
|
-
title: "quiet-otter",
|
|
1864
|
-
text: "Started: add retries with backoff around the upstream fetch",
|
|
1865
|
-
createdAt: at2(9)
|
|
1866
|
-
}
|
|
1867
|
-
],
|
|
1868
|
-
"scoop-otter": [
|
|
1869
|
-
{
|
|
1870
|
-
id: "m-o1",
|
|
1871
|
-
role: "user",
|
|
1872
|
-
origin: "agent",
|
|
1873
|
-
from: "harbor",
|
|
1874
|
-
text: "Wrap the upstream fetch in src/routes/forecast.ts with three retries and exponential backoff.",
|
|
1875
|
-
createdAt: at2(9)
|
|
1876
|
-
},
|
|
1877
|
-
{
|
|
1878
|
-
id: "m-o2",
|
|
1879
|
-
role: "assistant",
|
|
1880
|
-
status: "done",
|
|
1881
|
-
createdAt: at2(10),
|
|
1882
|
-
parts: [
|
|
1883
|
-
{
|
|
1884
|
-
type: "tool",
|
|
1885
|
-
tool: {
|
|
1886
|
-
id: "t-o1",
|
|
1887
|
-
name: "write_file",
|
|
1888
|
-
title: "Add retry()",
|
|
1889
|
-
input: "/workspace/harbor/src/lib/retry.ts",
|
|
1890
|
-
output: "Wrote 12 lines",
|
|
1891
|
-
status: "done",
|
|
1892
|
-
paths: ["/workspace/harbor/src/lib/retry.ts"]
|
|
1893
|
-
}
|
|
1894
|
-
},
|
|
1895
|
-
{
|
|
1896
|
-
type: "tool",
|
|
1897
|
-
tool: {
|
|
1898
|
-
id: "t-o2",
|
|
1899
|
-
name: "edit_file",
|
|
1900
|
-
title: "Use retry() for the upstream call",
|
|
1901
|
-
input: "/workspace/harbor/src/routes/forecast.ts",
|
|
1902
|
-
output: "Replaced 1 block",
|
|
1903
|
-
status: "done",
|
|
1904
|
-
paths: ["/workspace/harbor/src/routes/forecast.ts"]
|
|
1905
|
-
}
|
|
1906
|
-
},
|
|
1907
|
-
{ type: "text", text: "Both edits are in. Running the tests next." }
|
|
1908
|
-
]
|
|
1909
|
-
}
|
|
1910
|
-
],
|
|
1911
|
-
"scoop-heron": [],
|
|
1912
|
-
"scoop-wren": [],
|
|
1913
|
-
"cone-tides": [
|
|
1914
|
-
{
|
|
1915
|
-
id: "m-f1",
|
|
1916
|
-
role: "user",
|
|
1917
|
-
text: "Add a tide table endpoint next to the forecast, with high and low water per day.",
|
|
1918
|
-
createdAt: at2(-2900)
|
|
1919
|
-
},
|
|
1920
|
-
{
|
|
1921
|
-
id: "m-f2",
|
|
1922
|
-
role: "assistant",
|
|
1923
|
-
status: "done",
|
|
1924
|
-
createdAt: at2(-2898),
|
|
1925
|
-
parts: [
|
|
1926
|
-
{
|
|
1927
|
-
type: "text",
|
|
1928
|
-
text: "Added `/v1/tides?city=` with the next seven days of high and low water. It reads from the same upstream as the forecast and shares its cache."
|
|
1929
|
-
}
|
|
1930
|
-
]
|
|
1931
|
-
}
|
|
1932
|
-
],
|
|
1933
|
-
"cone-release": [
|
|
1934
|
-
{
|
|
1935
|
-
id: "m-r1",
|
|
1936
|
-
role: "lick",
|
|
1937
|
-
channel: "cron",
|
|
1938
|
-
title: "Every Friday 09:00",
|
|
1939
|
-
text: "Draft the release notes for example/harbor",
|
|
1940
|
-
createdAt: at2(-1440)
|
|
1941
|
-
},
|
|
1942
|
-
{
|
|
1943
|
-
id: "m-r2",
|
|
1944
|
-
role: "assistant",
|
|
1945
|
-
status: "done",
|
|
1946
|
-
createdAt: at2(-1438),
|
|
1947
|
-
parts: [
|
|
1948
|
-
{
|
|
1949
|
-
type: "tool",
|
|
1950
|
-
tool: {
|
|
1951
|
-
id: "t-r1",
|
|
1952
|
-
name: "bash",
|
|
1953
|
-
title: "Collect merged pull requests",
|
|
1954
|
-
input: "collect.jsh example/harbor v1.8.0",
|
|
1955
|
-
output: "41 fix Return 400 when the city is missing\n43 feature Add /health\n44 chore Update wrangler to 4.40.0",
|
|
1956
|
-
status: "done",
|
|
1957
|
-
paths: ["/workspace/skills/release-notes/scripts/collect.jsh"]
|
|
1958
|
-
}
|
|
1959
|
-
},
|
|
1960
|
-
{
|
|
1961
|
-
type: "text",
|
|
1962
|
-
text: "## harbor 1.9.0\n\n**Features**\n- Added a `/health` endpoint.\n\n**Fixes**\n- Requests without a city now get a 400 instead of a 500.\n\n**Chores**\n- Updated wrangler to 4.40.0."
|
|
1963
|
-
}
|
|
1964
|
-
]
|
|
1965
|
-
},
|
|
1966
|
-
{
|
|
1967
|
-
id: "m-r3",
|
|
1968
|
-
role: "lick",
|
|
1969
|
-
channel: "cron",
|
|
1970
|
-
severity: "error",
|
|
1971
|
-
title: "Crontab",
|
|
1972
|
-
text: "Crontab line 3 is invalid",
|
|
1973
|
-
body: "30 25 * * * sweep-drafts\nThe hour is 25. It must be 0 to 23.",
|
|
1974
|
-
createdAt: at2(-1430)
|
|
1975
|
-
},
|
|
1976
|
-
{
|
|
1977
|
-
id: "m-r4",
|
|
1978
|
-
role: "lick",
|
|
1979
|
-
channel: "fswatch",
|
|
1980
|
-
severity: "warn",
|
|
1981
|
-
title: "/workspace/notes",
|
|
1982
|
-
text: "File watch on /workspace/notes stopped; retrying",
|
|
1983
|
-
createdAt: at2(-1429)
|
|
1984
|
-
},
|
|
1985
|
-
{
|
|
1986
|
-
id: "m-r5",
|
|
1987
|
-
role: "lick",
|
|
1988
|
-
channel: "workflow",
|
|
1989
|
-
title: "Release report",
|
|
1990
|
-
text: "The weekly release report is ready",
|
|
1991
|
-
body: "Summary: harbor 1.9.0 shipped three changes this week, the /health endpoint, a 400 for requests without a city and a wrangler update, with no failed deploys, no rollbacks and two flaky test runs in the forecast suite that passed on retry, so the next release can go out on Friday as planned once the docs pass lands.",
|
|
1992
|
-
createdAt: at2(-1428)
|
|
1993
|
-
}
|
|
1994
|
-
],
|
|
1995
|
-
"cone-triage": [
|
|
1996
|
-
{
|
|
1997
|
-
id: "m-t1",
|
|
1998
|
-
role: "system",
|
|
1999
|
-
kind: "compaction",
|
|
2000
|
-
trigger: "threshold",
|
|
2001
|
-
state: "summarized",
|
|
2002
|
-
title: "Context compacted",
|
|
2003
|
-
text: "41 earlier messages were summarized to stay under the context limit.",
|
|
2004
|
-
createdAt: at2(-30)
|
|
2005
|
-
},
|
|
2006
|
-
{
|
|
2007
|
-
id: "m-t2",
|
|
2008
|
-
role: "assistant",
|
|
2009
|
-
status: "done",
|
|
2010
|
-
createdAt: at2(-20),
|
|
2011
|
-
parts: [
|
|
2012
|
-
{
|
|
2013
|
-
type: "text",
|
|
2014
|
-
text: "Two messages need a decision from you:\n\n- A vendor asks to move Thursday\u2019s call to Friday.\n- The staging certificate expires in 6 days. Should I open a ticket?"
|
|
2015
|
-
}
|
|
2016
|
-
]
|
|
2017
|
-
},
|
|
2018
|
-
{
|
|
2019
|
-
id: "m-t3",
|
|
2020
|
-
role: "system",
|
|
2021
|
-
kind: "error",
|
|
2022
|
-
action: "login",
|
|
2023
|
-
title: "Mail connector",
|
|
2024
|
-
text: "The connector returned 401. Reconnect the account in Settings.",
|
|
2025
|
-
createdAt: at2(-19)
|
|
2026
|
-
},
|
|
2027
|
-
{
|
|
2028
|
-
id: "m-t4",
|
|
2029
|
-
role: "system",
|
|
2030
|
-
kind: "error",
|
|
2031
|
-
action: "login",
|
|
2032
|
-
account: "mcp:tickets",
|
|
2033
|
-
title: "Tickets connector",
|
|
2034
|
-
text: "The tickets server rejected its token.",
|
|
2035
|
-
createdAt: at2(-18)
|
|
2036
|
-
}
|
|
2037
|
-
]
|
|
2038
|
-
};
|
|
2039
|
-
var tabs = [
|
|
2040
|
-
{
|
|
2041
|
-
id: "tab-preview",
|
|
2042
|
-
title: "harbor \xB7 localhost",
|
|
2043
|
-
url: "http://localhost:8787/forecast?city=Lisbon",
|
|
2044
|
-
status: "complete",
|
|
2045
|
-
agentId: "cone-harbor"
|
|
2046
|
-
},
|
|
2047
|
-
{
|
|
2048
|
-
id: "tab-docs",
|
|
2049
|
-
title: "Forecast API reference",
|
|
2050
|
-
url: "https://api.example.com/docs/v2/forecast",
|
|
2051
|
-
status: "complete",
|
|
2052
|
-
agentId: null,
|
|
2053
|
-
favicon: `data:image/svg+xml;charset=utf-8,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect width="16" height="16" rx="3" fill="#7a5af8"/><text x="8" y="12" font-family="sans-serif" font-size="11" font-weight="700" fill="#ffffff" text-anchor="middle">A</text></svg>')}`
|
|
2054
|
-
},
|
|
2055
|
-
{
|
|
2056
|
-
id: "tab-pull",
|
|
2057
|
-
title: "Add retries for upstream timeouts \xB7 Pull request #58",
|
|
2058
|
-
url: "https://git.example.com/example/harbor/pull/58",
|
|
2059
|
-
status: "complete",
|
|
2060
|
-
agentId: "scoop-otter",
|
|
2061
|
-
favicon: `data:image/svg+xml;charset=utf-8,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect width="16" height="16" rx="3" fill="#0a7a43"/><text x="8" y="12" font-family="sans-serif" font-size="11" font-weight="700" fill="#ffffff" text-anchor="middle">G</text></svg>')}`
|
|
2062
|
-
}
|
|
2063
|
-
];
|
|
2064
|
-
var terminals = [
|
|
2065
|
-
{ id: "term-1", title: "bash", cwd: "/workspace/harbor", agentId: null }
|
|
2066
|
-
];
|
|
2067
|
-
|
|
2068
534
|
// src/dummy/guest.ts
|
|
2069
535
|
var guestOnly = "a guest can only watch and send messages";
|
|
2070
536
|
var guestReview = "Messages from guests need a review, and this agent can't review them yet.";
|
|
@@ -2096,13 +562,13 @@ var forecastSprinkle = {
|
|
|
2096
562
|
html: "<style>body { font-family: var(--s2-font-family); color: var(--s2-content-default); padding: var(--s2-spacing-200) var(--s2-spacing-300); } table { border-collapse: collapse; } td { padding: 4px 12px 4px 0; }</style><table><tr><td>Fri</td><td>21\xB0</td><td>Sunny</td></tr><tr><td>Sat</td><td>19\xB0</td><td>Cloudy</td></tr><tr><td>Sun</td><td>18\xB0</td><td>Showers</td></tr></table><script>window.slicc.lick({ action: 'viewed', data: { rows: 3 } });<\/script>"
|
|
2097
563
|
};
|
|
2098
564
|
function guestTranscript(now2 = Date.now(), twin = false) {
|
|
2099
|
-
const
|
|
565
|
+
const at = (minutes2) => now2 - minutes2 * minute;
|
|
2100
566
|
const namesake = twin ? [
|
|
2101
567
|
{
|
|
2102
568
|
id: "g-9",
|
|
2103
569
|
role: "user",
|
|
2104
570
|
text: "Same name, another seat.",
|
|
2105
|
-
createdAt:
|
|
571
|
+
createdAt: at(8),
|
|
2106
572
|
origin: "guest",
|
|
2107
573
|
from: guestLabel,
|
|
2108
574
|
seat: "seat-mara-2",
|
|
@@ -2114,13 +580,13 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2114
580
|
id: "g-1",
|
|
2115
581
|
role: "user",
|
|
2116
582
|
text: "Check the Porto forecast endpoint before Mara joins.",
|
|
2117
|
-
createdAt:
|
|
583
|
+
createdAt: at(42)
|
|
2118
584
|
},
|
|
2119
585
|
{
|
|
2120
586
|
id: "g-2",
|
|
2121
587
|
role: "assistant",
|
|
2122
588
|
status: "done",
|
|
2123
|
-
createdAt:
|
|
589
|
+
createdAt: at(41),
|
|
2124
590
|
model: "claude-sonnet-5-5",
|
|
2125
591
|
parts: [
|
|
2126
592
|
{ type: "text", text: "I read the handler and ran its tests." },
|
|
@@ -2155,7 +621,7 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2155
621
|
id: "g-3",
|
|
2156
622
|
role: "user",
|
|
2157
623
|
text: "Hi! Could you add the weekend to the forecast table?",
|
|
2158
|
-
createdAt:
|
|
624
|
+
createdAt: at(30),
|
|
2159
625
|
origin: "guest",
|
|
2160
626
|
from: guestLabel,
|
|
2161
627
|
seat: "seat-mara",
|
|
@@ -2165,7 +631,7 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2165
631
|
id: "g-4",
|
|
2166
632
|
role: "assistant",
|
|
2167
633
|
status: "done",
|
|
2168
|
-
createdAt:
|
|
634
|
+
createdAt: at(29),
|
|
2169
635
|
model: "claude-sonnet-5-5",
|
|
2170
636
|
parts: [
|
|
2171
637
|
{
|
|
@@ -2187,7 +653,7 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2187
653
|
id: "g-5",
|
|
2188
654
|
role: "user",
|
|
2189
655
|
text: "Does it cover Lisbon too?",
|
|
2190
|
-
createdAt:
|
|
656
|
+
createdAt: at(12),
|
|
2191
657
|
origin: "guest",
|
|
2192
658
|
from: otherGuest,
|
|
2193
659
|
seat: "seat-jonas",
|
|
@@ -2197,7 +663,7 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2197
663
|
id: "g-6",
|
|
2198
664
|
role: "assistant",
|
|
2199
665
|
status: "done",
|
|
2200
|
-
createdAt:
|
|
666
|
+
createdAt: at(11),
|
|
2201
667
|
model: "claude-sonnet-5-5",
|
|
2202
668
|
parts: [
|
|
2203
669
|
{ type: "text", text: "Not yet. I can add it." },
|
|
@@ -2221,7 +687,7 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2221
687
|
text: "wants to fetch the Lisbon forecast",
|
|
2222
688
|
body: "curl -s https://api.example.com/forecast?city=lisbon",
|
|
2223
689
|
state: "pending",
|
|
2224
|
-
createdAt:
|
|
690
|
+
createdAt: at(10),
|
|
2225
691
|
expiresAt: now2 + 20 * minute
|
|
2226
692
|
},
|
|
2227
693
|
{
|
|
@@ -2231,7 +697,7 @@ function guestTranscript(now2 = Date.now(), twin = false) {
|
|
|
2231
697
|
title: "The model is busy",
|
|
2232
698
|
text: "The provider asked to slow down.",
|
|
2233
699
|
action: "retry",
|
|
2234
|
-
createdAt:
|
|
700
|
+
createdAt: at(9)
|
|
2235
701
|
},
|
|
2236
702
|
...namesake
|
|
2237
703
|
];
|
|
@@ -2630,14 +1096,14 @@ var importCones = [
|
|
|
2630
1096
|
}
|
|
2631
1097
|
];
|
|
2632
1098
|
var idle = { phase: "idle", done: 0, total: 0, text: "" };
|
|
2633
|
-
function conversation(id, title,
|
|
1099
|
+
function conversation(id, title, at) {
|
|
2634
1100
|
return [
|
|
2635
|
-
{ id: `${id}-1`, role: "user", text: title, createdAt:
|
|
1101
|
+
{ id: `${id}-1`, role: "user", text: title, createdAt: at },
|
|
2636
1102
|
{
|
|
2637
1103
|
id: `${id}-2`,
|
|
2638
1104
|
role: "assistant",
|
|
2639
1105
|
status: "done",
|
|
2640
|
-
createdAt:
|
|
1106
|
+
createdAt: at + 6e4,
|
|
2641
1107
|
parts: [
|
|
2642
1108
|
{ type: "text", text: "Imported from SLICC 6. The earlier turns are in the report." }
|
|
2643
1109
|
]
|
|
@@ -2723,7 +1189,7 @@ var DummyImport = class {
|
|
|
2723
1189
|
};
|
|
2724
1190
|
|
|
2725
1191
|
// src/dummy/script.ts
|
|
2726
|
-
var
|
|
1192
|
+
var units = "/workspace/harbor/src/lib/units.ts";
|
|
2727
1193
|
var kelvin = `
|
|
2728
1194
|
export function toKelvin(celsius: number): number {
|
|
2729
1195
|
return Math.round((celsius + 273.15) * 100) / 100;
|
|
@@ -2770,29 +1236,29 @@ function edit() {
|
|
|
2770
1236
|
type: "tool",
|
|
2771
1237
|
name: "read_file",
|
|
2772
1238
|
title: "Read the unit helpers",
|
|
2773
|
-
input:
|
|
1239
|
+
input: units,
|
|
2774
1240
|
output: "export function toCelsius(fahrenheit: number): number { \u2026 }\nexport function toFahrenheit(celsius: number): number { \u2026 }",
|
|
2775
|
-
paths: [
|
|
1241
|
+
paths: [units],
|
|
2776
1242
|
ticks: 10
|
|
2777
1243
|
},
|
|
2778
1244
|
{
|
|
2779
1245
|
type: "tool",
|
|
2780
1246
|
name: "edit_file",
|
|
2781
1247
|
title: "Add toKelvin()",
|
|
2782
|
-
input:
|
|
1248
|
+
input: units,
|
|
2783
1249
|
output: "Appended 4 lines",
|
|
2784
|
-
paths: [
|
|
1250
|
+
paths: [units],
|
|
2785
1251
|
ticks: 20,
|
|
2786
1252
|
effect: async ({ files: files3, agentId }) => {
|
|
2787
|
-
const before = await files3.read(
|
|
2788
|
-
await files3.write(
|
|
1253
|
+
const before = await files3.read(units);
|
|
1254
|
+
await files3.write(units, before + kelvin, agentId);
|
|
2789
1255
|
}
|
|
2790
1256
|
},
|
|
2791
1257
|
{
|
|
2792
1258
|
type: "part",
|
|
2793
1259
|
part: {
|
|
2794
1260
|
type: "diff",
|
|
2795
|
-
path:
|
|
1261
|
+
path: units,
|
|
2796
1262
|
before: "export function toFahrenheit(celsius: number): number {\n return Math.round((celsius * 9) / 5 + 32);\n}\n",
|
|
2797
1263
|
after: `export function toFahrenheit(celsius: number): number {
|
|
2798
1264
|
return Math.round((celsius * 9) / 5 + 32);
|
|
@@ -2973,7 +1439,7 @@ function look(attachments) {
|
|
|
2973
1439
|
}
|
|
2974
1440
|
];
|
|
2975
1441
|
}
|
|
2976
|
-
var
|
|
1442
|
+
var suggestions = [
|
|
2977
1443
|
[/\b(debug|investigate)\b/, "Add the unit to the cache key"],
|
|
2978
1444
|
[/\btests?\b/, "Open a pull request with the fix"],
|
|
2979
1445
|
[/\b(fix|edit|add|change)\b/, "Run the tests"],
|
|
@@ -2982,7 +1448,7 @@ var suggestions2 = [
|
|
|
2982
1448
|
];
|
|
2983
1449
|
function script(prompt, attachments = []) {
|
|
2984
1450
|
const text = prompt.toLowerCase();
|
|
2985
|
-
const suggestion =
|
|
1451
|
+
const suggestion = suggestions.find(([pattern]) => pattern.test(text))?.[1] ?? "Run the tests";
|
|
2986
1452
|
if (attachments.length > 0) return { steps: look(attachments), suggestion };
|
|
2987
1453
|
if (/\bforbidden\b/.test(text)) return { steps: filtered(), suggestion };
|
|
2988
1454
|
if (/\b(ask|choose|decide)\b/.test(text)) return { steps: ask(), suggestion };
|
|
@@ -3139,7 +1605,7 @@ var DummyAgent = class extends Emitter {
|
|
|
3139
1605
|
}
|
|
3140
1606
|
async #tool(agentId, reply, step, run) {
|
|
3141
1607
|
this.#status(agentId, "working");
|
|
3142
|
-
const
|
|
1608
|
+
const tool = {
|
|
3143
1609
|
id: this.#id("t"),
|
|
3144
1610
|
name: step.name,
|
|
3145
1611
|
title: step.title,
|
|
@@ -3148,12 +1614,12 @@ var DummyAgent = class extends Emitter {
|
|
|
3148
1614
|
status: "running",
|
|
3149
1615
|
paths: step.paths
|
|
3150
1616
|
};
|
|
3151
|
-
reply.parts.push({ type: "tool", tool
|
|
1617
|
+
reply.parts.push({ type: "tool", tool });
|
|
3152
1618
|
this.emit("message", { agentId, message: reply });
|
|
3153
1619
|
await this.#clock.sleep(step.ticks * run.pace);
|
|
3154
1620
|
const done = run.stopped ? { status: "cancelled", output: "Stopped" } : { status: "done", output: step.output };
|
|
3155
1621
|
if (!run.stopped) await step.effect?.({ ...this.#effects, agentId });
|
|
3156
|
-
Object.assign(
|
|
1622
|
+
Object.assign(tool, done);
|
|
3157
1623
|
this.emit("message", { agentId, message: reply });
|
|
3158
1624
|
}
|
|
3159
1625
|
async #step(agentId, reply, step, run) {
|
|
@@ -3337,9 +1803,9 @@ var DummyAgent = class extends Emitter {
|
|
|
3337
1803
|
}
|
|
3338
1804
|
async rewind(agentId, messageId) {
|
|
3339
1805
|
const messages = this.#messages.get(agentId);
|
|
3340
|
-
const
|
|
3341
|
-
if (!messages ||
|
|
3342
|
-
const turn = messages.slice(0,
|
|
1806
|
+
const at = messages?.findIndex((message) => message.id === messageId) ?? -1;
|
|
1807
|
+
if (!messages || at < 0) return null;
|
|
1808
|
+
const turn = messages.slice(0, at).findLastIndex((message) => message.role === "user");
|
|
3343
1809
|
if (turn < 0) return null;
|
|
3344
1810
|
const user = messages[turn];
|
|
3345
1811
|
const turns = messages.splice(turn).filter((message) => message.role === "user").length;
|
|
@@ -5006,17 +3472,17 @@ var DummyFiles = class extends Emitter {
|
|
|
5006
3472
|
if (mount.scenario === "fail") throw new Error("The folder couldn\u2019t be mounted.");
|
|
5007
3473
|
if (point !== void 0 && !this.#needs.has(point))
|
|
5008
3474
|
throw new Error(`Nothing is waiting for a folder at ${point}.`);
|
|
5009
|
-
const
|
|
3475
|
+
const at = point ?? mount.point;
|
|
5010
3476
|
const files3 = [];
|
|
5011
3477
|
for (const [path, text] of Object.entries(mount.files)) {
|
|
5012
|
-
const file = `${
|
|
3478
|
+
const file = `${at}/${path}`;
|
|
5013
3479
|
this.#put(file, text, Date.now());
|
|
5014
3480
|
files3.push(file);
|
|
5015
3481
|
}
|
|
5016
|
-
this.#mounted.add(
|
|
5017
|
-
this.#needs.delete(
|
|
3482
|
+
this.#mounted.add(at);
|
|
3483
|
+
this.#needs.delete(at);
|
|
5018
3484
|
this.#remount(files3);
|
|
5019
|
-
return
|
|
3485
|
+
return at;
|
|
5020
3486
|
}
|
|
5021
3487
|
async #eject(path) {
|
|
5022
3488
|
await this.#clock.sleep();
|
|
@@ -5046,29 +3512,29 @@ var DummyMemory = class extends Emitter {
|
|
|
5046
3512
|
#next = 1;
|
|
5047
3513
|
constructor(memories2, scopes = () => [{ id: "global", label: "Everyone" }]) {
|
|
5048
3514
|
super();
|
|
5049
|
-
this.#memories = memories2.map((
|
|
3515
|
+
this.#memories = memories2.map((memory) => ({ ...memory }));
|
|
5050
3516
|
this.#scopes = scopes;
|
|
5051
3517
|
}
|
|
5052
3518
|
scopes() {
|
|
5053
3519
|
return this.#scopes();
|
|
5054
3520
|
}
|
|
5055
3521
|
list() {
|
|
5056
|
-
return this.#memories.map((
|
|
3522
|
+
return this.#memories.map((memory) => ({ ...memory }));
|
|
5057
3523
|
}
|
|
5058
3524
|
save(draft) {
|
|
5059
|
-
const
|
|
3525
|
+
const memory = {
|
|
5060
3526
|
...draft,
|
|
5061
3527
|
id: draft.id ?? `mem-new-${this.#next++}`,
|
|
5062
3528
|
updatedAt: Date.now()
|
|
5063
3529
|
};
|
|
5064
|
-
const
|
|
5065
|
-
if (
|
|
5066
|
-
else this.#memories[
|
|
3530
|
+
const index = this.#memories.findIndex((candidate) => candidate.id === memory.id);
|
|
3531
|
+
if (index < 0) this.#memories.push(memory);
|
|
3532
|
+
else this.#memories[index] = memory;
|
|
5067
3533
|
this.emit("memories", this.list());
|
|
5068
|
-
return { ...
|
|
3534
|
+
return { ...memory };
|
|
5069
3535
|
}
|
|
5070
3536
|
remove(id) {
|
|
5071
|
-
this.#memories = this.#memories.filter((
|
|
3537
|
+
this.#memories = this.#memories.filter((memory) => memory.id !== id);
|
|
5072
3538
|
this.emit("memories", this.list());
|
|
5073
3539
|
}
|
|
5074
3540
|
};
|
|
@@ -5846,8 +4312,8 @@ var DummyNetwork = class extends Emitter {
|
|
|
5846
4312
|
...device,
|
|
5847
4313
|
folders: device.folders.map((folder) => {
|
|
5848
4314
|
const { mounted: _, ...rest } = folder;
|
|
5849
|
-
const
|
|
5850
|
-
return
|
|
4315
|
+
const at = mounted2(folder);
|
|
4316
|
+
return at ? { ...rest, mounted: at } : rest;
|
|
5851
4317
|
})
|
|
5852
4318
|
} : device
|
|
5853
4319
|
);
|
|
@@ -6568,9 +5034,9 @@ function packageFixtures(scenario) {
|
|
|
6568
5034
|
79e5
|
|
6569
5035
|
)
|
|
6570
5036
|
];
|
|
6571
|
-
const
|
|
5037
|
+
const at = (id) => items.findIndex((item) => item.id === id);
|
|
6572
5038
|
const patch = (id, change) => {
|
|
6573
|
-
items[
|
|
5039
|
+
items[at(id)] = { ...items[at(id)], ...change };
|
|
6574
5040
|
};
|
|
6575
5041
|
patch("python", {
|
|
6576
5042
|
state: "installed",
|
|
@@ -7046,13 +5512,25 @@ function vaultPurged(now2 = Date.now()) {
|
|
|
7046
5512
|
type: "endpoints",
|
|
7047
5513
|
seq: 13,
|
|
7048
5514
|
id: "audit-13",
|
|
7049
|
-
at: now2 -
|
|
5515
|
+
at: now2 - 2 * minute3,
|
|
7050
5516
|
uid: null,
|
|
7051
5517
|
tag: null,
|
|
7052
5518
|
purged: [
|
|
7053
|
-
{ provider: "openai",
|
|
7054
|
-
{ provider: "anthropic",
|
|
7055
|
-
]
|
|
5519
|
+
{ provider: "openai", host: "models.harbor.example" },
|
|
5520
|
+
{ provider: "anthropic", host: "relay.harbor.example" }
|
|
5521
|
+
],
|
|
5522
|
+
adopted: [{ server: "docs", host: "docs.example" }]
|
|
5523
|
+
};
|
|
5524
|
+
}
|
|
5525
|
+
function vaultPinned(now2 = Date.now()) {
|
|
5526
|
+
return {
|
|
5527
|
+
type: "endpoints",
|
|
5528
|
+
seq: 14,
|
|
5529
|
+
id: "audit-14",
|
|
5530
|
+
at: now2 - minute3,
|
|
5531
|
+
uid: null,
|
|
5532
|
+
tag: null,
|
|
5533
|
+
pinned: { name: "DOCS_MCP_TOKEN", server: "docs", host: "docs.example" }
|
|
7056
5534
|
};
|
|
7057
5535
|
}
|
|
7058
5536
|
function vaultPolicy(now2 = Date.now()) {
|
|
@@ -7146,7 +5624,7 @@ function vaultFixtures(scenario, now2 = Date.now()) {
|
|
|
7146
5624
|
refused: policy ? vaultRefused() : [],
|
|
7147
5625
|
audit: empty ? [] : [
|
|
7148
5626
|
...policy ? [vaultPolicy(now2)] : [],
|
|
7149
|
-
...scenario === "purged" ? [vaultPurged(now2)] : [],
|
|
5627
|
+
...scenario === "purged" ? [vaultPinned(now2), vaultPurged(now2)] : [],
|
|
7150
5628
|
...vaultAudit(now2)
|
|
7151
5629
|
],
|
|
7152
5630
|
auditMore: !empty,
|
|
@@ -7236,6 +5714,8 @@ var DummyVault = class extends Emitter {
|
|
|
7236
5714
|
#serial = 0;
|
|
7237
5715
|
buttons = [];
|
|
7238
5716
|
puts = [];
|
|
5717
|
+
endpointChanges = [];
|
|
5718
|
+
#refuses;
|
|
7239
5719
|
cancelled = [];
|
|
7240
5720
|
imports = [];
|
|
7241
5721
|
exported = 0;
|
|
@@ -7247,6 +5727,16 @@ var DummyVault = class extends Emitter {
|
|
|
7247
5727
|
this.#list = { ...list, secrets: [...list.secrets, ...records] };
|
|
7248
5728
|
this.#time = time;
|
|
7249
5729
|
this.#available = scenario === "waiting" ? "waiting" : "ready";
|
|
5730
|
+
this.#refuses = scenario === "endpoints-fail";
|
|
5731
|
+
}
|
|
5732
|
+
async endpoints(change) {
|
|
5733
|
+
await this.#time.sleep(5);
|
|
5734
|
+
if (this.#refuses)
|
|
5735
|
+
throw vaultFailure(
|
|
5736
|
+
"EINVAL",
|
|
5737
|
+
"The vault refused that endpoint. Check the address and try again."
|
|
5738
|
+
);
|
|
5739
|
+
this.endpointChanges.push(change);
|
|
7250
5740
|
}
|
|
7251
5741
|
status() {
|
|
7252
5742
|
return this.#available === "waiting" ? null : this.#status;
|
|
@@ -7381,6 +5871,18 @@ var DummyVault = class extends Emitter {
|
|
|
7381
5871
|
this.cancelled.push(request);
|
|
7382
5872
|
return "cancelled";
|
|
7383
5873
|
}
|
|
5874
|
+
async unpin(names) {
|
|
5875
|
+
await this.#time.sleep(5);
|
|
5876
|
+
this.#unlocked();
|
|
5877
|
+
for (const name2 of names) {
|
|
5878
|
+
const pin = this.#list.audit.find(
|
|
5879
|
+
(entry) => entry.type === "endpoints" && entry.pinned?.name === name2
|
|
5880
|
+
);
|
|
5881
|
+
if (pin?.type === "endpoints" && pin.pinned)
|
|
5882
|
+
this.#log(name2, { type: "endpoints", unpinned: pin.pinned });
|
|
5883
|
+
}
|
|
5884
|
+
this.emit("list", this.#list);
|
|
5885
|
+
}
|
|
7384
5886
|
async remove(name2, kind = "secret") {
|
|
7385
5887
|
await this.#time.sleep(5);
|
|
7386
5888
|
const secret = { ...this.#find(name2, kind), state: "removed", removedAt: Date.now() };
|
|
@@ -7475,6 +5977,13 @@ function vaultAccounts(accounts2) {
|
|
|
7475
5977
|
(account) => account.auth === "api-key" || account.id === "github" ? { ...account, store: "vault" } : account
|
|
7476
5978
|
);
|
|
7477
5979
|
}
|
|
5980
|
+
function settingsAccounts(accounts2, providers) {
|
|
5981
|
+
const all = accounts2 === "vault" ? vaultAccounts(accounts) : accounts;
|
|
5982
|
+
if (providers === "all") return providerAccounts(all);
|
|
5983
|
+
return all.map(
|
|
5984
|
+
(account) => providers === "none" && account.group !== "mcp" ? { ...account, status: "disconnected", identity: "" } : account
|
|
5985
|
+
);
|
|
5986
|
+
}
|
|
7478
5987
|
function guestsPort(guests, time) {
|
|
7479
5988
|
return guests === "off" ? {} : { guests: new DummyGuests(guests, time) };
|
|
7480
5989
|
}
|
|
@@ -7551,14 +6060,13 @@ function createDummyModel({
|
|
|
7551
6060
|
settings: new DummySettings(
|
|
7552
6061
|
defaults,
|
|
7553
6062
|
providers === "none" ? [...models, ...laterModels] : models,
|
|
7554
|
-
(accounts2
|
|
7555
|
-
(account) => providers === "none" && account.group !== "mcp" ? { ...account, status: "disconnected", identity: "" } : account
|
|
7556
|
-
),
|
|
6063
|
+
settingsAccounts(accounts2, providers),
|
|
7557
6064
|
storage,
|
|
7558
6065
|
time,
|
|
7559
6066
|
void 0,
|
|
7560
6067
|
providers === "none",
|
|
7561
|
-
login
|
|
6068
|
+
login,
|
|
6069
|
+
providers === "all" ? providerFixtures : void 0
|
|
7562
6070
|
),
|
|
7563
6071
|
memory: new DummyMemory(memories, () => [
|
|
7564
6072
|
{ id: "global", label: "Everyone" },
|
|
@@ -7646,6 +6154,9 @@ export {
|
|
|
7646
6154
|
pairingCode,
|
|
7647
6155
|
pairingRefused,
|
|
7648
6156
|
pairingStopped,
|
|
6157
|
+
providerAccount,
|
|
6158
|
+
providerAccounts,
|
|
6159
|
+
providerFixtures,
|
|
7649
6160
|
tabsFixtures,
|
|
7650
6161
|
tailnetFixtures,
|
|
7651
6162
|
updateFixtures,
|
|
@@ -7653,6 +6164,7 @@ export {
|
|
|
7653
6164
|
vaultFixtures,
|
|
7654
6165
|
vaultNotice,
|
|
7655
6166
|
vaultOlder,
|
|
6167
|
+
vaultPinned,
|
|
7656
6168
|
vaultPolicy,
|
|
7657
6169
|
vaultPurged,
|
|
7658
6170
|
vaultRefused,
|