@agentshouse/kit 0.1.0-alpha.4 → 0.1.0-alpha.40
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 +41 -1671
- package/bin/connect-linux.sh +501 -53
- package/bin/connect-windows.ps1 +430 -0
- package/bin/kit.js +200 -159
- package/bootstrap.json +28 -0
- package/docs/configuration.md +104 -0
- package/docs/google.md +33 -0
- package/docs/linux.md +77 -0
- package/docs/macos.md +26 -0
- package/docs/telegram.md +29 -0
- package/docs/windows.md +36 -0
- package/package.json +7 -2
- package/src/adapter-mcp.js +92 -12
- package/src/agents.js +63 -8
- package/src/binding.js +52 -0
- package/src/blocks.js +2 -26
- package/src/bridge.js +28 -14
- package/src/bytes.js +19 -8
- package/src/configuration.js +14 -211
- package/src/connector-login.js +3 -84
- package/src/contract.js +44 -12
- package/src/conversation-originals.js +1 -3
- package/src/delivery.js +4 -7
- package/src/directory.js +6 -26
- package/src/draft.js +4 -82
- package/src/git-main.js +138 -0
- package/src/git.js +62 -0
- package/src/google/calendar.js +39 -45
- package/src/google/drive.js +61 -186
- package/src/google/entries.js +349 -0
- package/src/google/gateway.js +3 -6
- package/src/google/gmail.js +33 -23
- package/src/google/oauth.js +106 -66
- package/src/google/source.js +21 -23
- package/src/host.js +6 -2
- package/src/house-main.js +65 -8
- package/src/house.js +446 -127
- package/src/how-we-work.md +25 -0
- package/src/inbound.js +2 -18
- package/src/kit-work-stream.js +120 -0
- package/src/launcher.js +46 -10
- package/src/local-files.js +3 -11
- package/src/login.js +27 -14
- package/src/mcp.js +18 -0
- package/src/mount.js +9 -18
- package/src/original.js +4 -4
- package/src/pacing.js +41 -0
- package/src/plan.js +37 -45
- package/src/repository.js +3 -61
- package/src/session-bin/git +2 -0
- package/src/setup.js +14 -15
- package/src/skill-set/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/src/skill-set/ask-matt/SKILL.md +91 -0
- package/src/skill-set/ask-matt/agents/openai.yaml +4 -0
- package/src/skill-set/code-review/LICENSE +21 -0
- package/src/skill-set/code-review/SKILL.md +89 -0
- package/src/skill-set/code-review/agents/openai.yaml +3 -0
- package/src/skill-set/codebase-design/DEEPENING.md +37 -0
- package/src/skill-set/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/src/skill-set/codebase-design/LICENSE +21 -0
- package/src/skill-set/codebase-design/SKILL.md +116 -0
- package/src/skill-set/codebase-design/agents/openai.yaml +3 -0
- package/src/skill-set/diagnosing-bugs/LICENSE +21 -0
- package/src/skill-set/diagnosing-bugs/SKILL.md +140 -0
- package/src/skill-set/diagnosing-bugs/agents/openai.yaml +3 -0
- package/src/skill-set/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/src/skill-set/domain-modeling/ADR-FORMAT.md +47 -0
- package/src/skill-set/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/src/skill-set/domain-modeling/LICENSE +21 -0
- package/src/skill-set/domain-modeling/SKILL.md +76 -0
- package/src/skill-set/domain-modeling/agents/openai.yaml +3 -0
- package/src/skill-set/grill-me/LICENSE +21 -0
- package/src/skill-set/grill-me/SKILL.md +8 -0
- package/src/skill-set/grill-me/agents/openai.yaml +4 -0
- package/src/skill-set/grill-with-docs/LICENSE +21 -0
- package/src/skill-set/grill-with-docs/SKILL.md +8 -0
- package/src/skill-set/grill-with-docs/agents/openai.yaml +4 -0
- package/src/skill-set/grilling/LICENSE +21 -0
- package/src/{starter-set → skill-set}/grilling/SKILL.md +1 -1
- package/src/skill-set/handoff/LICENSE +21 -0
- package/src/skill-set/handoff/SKILL.md +17 -0
- package/src/skill-set/handoff/agents/openai.yaml +4 -0
- package/src/skill-set/implement/LICENSE +21 -0
- package/src/skill-set/implement/SKILL.md +16 -0
- package/src/skill-set/implement/agents/openai.yaml +4 -0
- package/src/skill-set/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/src/skill-set/improve-codebase-architecture/LICENSE +21 -0
- package/src/skill-set/improve-codebase-architecture/SKILL.md +72 -0
- package/src/skill-set/improve-codebase-architecture/agents/openai.yaml +4 -0
- package/src/skill-set/prototype/LICENSE +21 -0
- package/src/skill-set/prototype/LOGIC.md +67 -0
- package/src/skill-set/prototype/SKILL.md +28 -0
- package/src/skill-set/prototype/UI.md +112 -0
- package/src/skill-set/prototype/agents/openai.yaml +3 -0
- package/src/skill-set/research/LICENSE +21 -0
- package/src/skill-set/research/SKILL.md +14 -0
- package/src/skill-set/research/agents/openai.yaml +3 -0
- package/src/skill-set/resolving-merge-conflicts/LICENSE +21 -0
- package/src/skill-set/resolving-merge-conflicts/SKILL.md +16 -0
- package/src/skill-set/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/src/skill-set/tdd/LICENSE +21 -0
- package/src/skill-set/tdd/SKILL.md +40 -0
- package/src/skill-set/tdd/agents/openai.yaml +3 -0
- package/src/skill-set/tdd/mocking.md +59 -0
- package/src/skill-set/tdd/tests.md +77 -0
- package/src/skill-set/teach/GLOSSARY-FORMAT.md +35 -0
- package/src/skill-set/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/src/skill-set/teach/LICENSE +21 -0
- package/src/skill-set/teach/MISSION-FORMAT.md +31 -0
- package/src/skill-set/teach/RESOURCES-FORMAT.md +32 -0
- package/src/skill-set/teach/SKILL.md +141 -0
- package/src/skill-set/teach/agents/openai.yaml +4 -0
- package/src/skill-set/to-questionnaire/LICENSE +21 -0
- package/src/skill-set/to-questionnaire/SKILL.md +55 -0
- package/src/skill-set/to-questionnaire/agents/openai.yaml +4 -0
- package/src/skill-set/to-spec/LICENSE +21 -0
- package/src/skill-set/to-spec/SKILL.md +76 -0
- package/src/skill-set/to-spec/agents/openai.yaml +4 -0
- package/src/skill-set/to-tickets/LICENSE +21 -0
- package/src/skill-set/to-tickets/SKILL.md +106 -0
- package/src/skill-set/to-tickets/agents/openai.yaml +4 -0
- package/src/skill-set/wait-what/LICENSE +21 -0
- package/src/skill-set/wait-what/SKILL.md +8 -0
- package/src/skill-set/wait-what/agents/openai.yaml +4 -0
- package/src/skill-set/wayfinder/LICENSE +21 -0
- package/src/skill-set/wayfinder/SKILL.md +129 -0
- package/src/skill-set/wayfinder/agents/openai.yaml +4 -0
- package/src/skill-set/writing-for-agents/LICENSE +21 -0
- package/src/skill-set/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/src/skill-set/writing-for-agents/SKILL.md +83 -0
- package/src/skill-set/writing-for-agents/agents/openai.yaml +3 -0
- package/src/skill-set.js +85 -0
- package/src/source.js +93 -54
- package/src/state.js +19 -1
- package/src/sync-library.js +7 -11
- package/src/sync-state.js +12 -3
- package/src/sync.js +142 -43
- package/src/tasks.js +92 -0
- package/src/telegram-view.js +217 -0
- package/src/telegram.js +204 -69
- package/src/turn-frames.js +69 -0
- package/src/worker.js +738 -189
- package/src/working-copy.js +1274 -0
- package/src/workspace.js +299 -88
- package/src/google/declared.js +0 -146
- package/src/google/id.js +0 -1
- package/src/instance/google.js +0 -1
- package/src/starter-set.js +0 -17
- /package/src/{starter-set/grilling → skill-set/ask-matt}/LICENSE +0 -0
- /package/src/{starter-set → skill-set}/grilling/agents/openai.yaml +0 -0
package/src/contract.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CLAUDE_PASTED_CODE, CODEX_DEVICE_CODE, GROK_DEVICE_CODE } from "./agent-logins.js";
|
|
2
2
|
|
|
3
|
-
export const KIT_CONTRACT_VERSION = "
|
|
3
|
+
export const KIT_CONTRACT_VERSION = "3";
|
|
4
4
|
export const KIT_CAPTURE_BATCH_MAX = 100;
|
|
5
5
|
export const COMMAND_INPUT_MAX_BYTES = 4 * 1024;
|
|
6
6
|
export const AUTHORIZATION_OUTPUT_MAX_BYTES = 4 * 1024;
|
|
@@ -21,10 +21,11 @@ export const COMMAND_ADAPTER_SESSION_TIMEOUT_MS = 30_000;
|
|
|
21
21
|
export const WORKER_EXIT_GRACE_MS = 5_000;
|
|
22
22
|
export const WORKER_EXIT_POLL_MS = 100;
|
|
23
23
|
export const WORKER_SUPERVISION_POLL_MS = 2_000;
|
|
24
|
-
export const WORKER_COMMAND_POLL_MS = 2_000;
|
|
25
|
-
export const WORKER_HEARTBEAT_MS = 30_000;
|
|
26
24
|
export const WORKER_COMMAND_BATCH_MAX = 10;
|
|
27
25
|
export const WORKER_RUN_TOKEN_MARGIN_MS = 60_000;
|
|
26
|
+
export const KIT_STREAM_RECONNECT_MS = 1_000;
|
|
27
|
+
export const KIT_STREAM_RECONNECT_MAX_MS = 30_000;
|
|
28
|
+
export const KIT_STREAM_REJOIN_MS = 2_000;
|
|
28
29
|
export const TELEGRAM_IMAGE_UPLOAD_CEILING_BYTES = 10 * 1024 * 1024;
|
|
29
30
|
export const CONVERSATION_FILE_TRANSFER_TIMEOUT_MS = 30_000;
|
|
30
31
|
export const WORKER_INHERITED_ENVIRONMENT = ["PATH", "HOME"];
|
|
@@ -40,17 +41,15 @@ export function runOutcomePath(run) {
|
|
|
40
41
|
return `/kit/runs/${encodeURIComponent(run)}/outcome`;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
export function runRequestPath(run) {
|
|
45
|
+
return `/kit/runs/${encodeURIComponent(run)}/request`;
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
export const KIT_SESSIONS_REPORT_PATH = "/kit/sessions/report";
|
|
44
49
|
export const KIT_SESSIONS_REPORT_MAX = 256;
|
|
45
50
|
|
|
46
|
-
export const ASSISTANT_TEXT_MAX_BYTES = 4096;
|
|
47
|
-
export const ASSISTANT_BLOCKS_MAX = 500;
|
|
48
51
|
export const ASSISTANT_TABLE_COLUMNS_MAX = 20;
|
|
49
52
|
|
|
50
|
-
export function assistantDraftsPath(workerSession) {
|
|
51
|
-
return `/kit/sessions/${encodeURIComponent(workerSession)}/drafts`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
53
|
export function assistantTextPath(workerSession) {
|
|
55
54
|
return `/kit/sessions/${encodeURIComponent(workerSession)}/assistant-messages`;
|
|
56
55
|
}
|
|
@@ -61,6 +60,10 @@ export const TELEGRAM_DELIVERY_BATCH_MAX = 10;
|
|
|
61
60
|
export const TELEGRAM_DOWNLOAD_CEILING_BYTES = 20 * 1024 * 1024;
|
|
62
61
|
export const TELEGRAM_BOT_API_TIMEOUT_MS = 30_000;
|
|
63
62
|
export const TELEGRAM_GATEWAY_POLL_MS = 2_000;
|
|
63
|
+
export const TELEGRAM_RICH_MESSAGE_CHARACTERS_MAX = 32_768;
|
|
64
|
+
export const TELEGRAM_MESSAGE_CHARACTERS_MAX = 4_096;
|
|
65
|
+
export const TELEGRAM_CAPTION_CHARACTERS_MAX = 1_024;
|
|
66
|
+
export const TELEGRAM_RICH_MESSAGE_BLOCKS_MAX = 500;
|
|
64
67
|
export const TELEGRAM_CONTROL_KEYBOARD_COMMANDS = ["/stop", "/kill"];
|
|
65
68
|
export const TELEGRAM_MEDIA_KINDS = [
|
|
66
69
|
"document",
|
|
@@ -114,6 +117,10 @@ export const BRIDGE_NONCE_ENVIRONMENT = "HOUSE_KIT_BRIDGE_NONCE";
|
|
|
114
117
|
export const BRIDGE_RECONNECT_MS = 200;
|
|
115
118
|
export const BRIDGE_RECONNECT_MAX_MS = 5_000;
|
|
116
119
|
|
|
120
|
+
export const HOUSE_CLI_LINE = "The agents.house house CLI is available: run house --help.";
|
|
121
|
+
export const HOW_WE_WORK_PATH = "/private/library/how-we-work.md";
|
|
122
|
+
export const SHELL_RENDER = "shell/1";
|
|
123
|
+
|
|
117
124
|
export const ACP_PROTOCOL = 1;
|
|
118
125
|
export const ACP_AUTH_REQUIRED = -32000;
|
|
119
126
|
export const ADAPTER_EXIT_GRACE_MS = 2_000;
|
|
@@ -195,7 +202,7 @@ export const ADAPTER_CONTRACTS = Object.freeze({
|
|
|
195
202
|
guidedLogin: CLAUDE_PASTED_CODE,
|
|
196
203
|
identity: Object.freeze({ agentInfo: "@agentclientprotocol/claude-agent-acp" }),
|
|
197
204
|
nativeQuestion: null,
|
|
198
|
-
sessionLoad:
|
|
205
|
+
sessionLoad: true,
|
|
199
206
|
sessionConfig: Object.freeze({ model: "model", effort: "effort" }),
|
|
200
207
|
capabilities: Object.freeze({
|
|
201
208
|
field_kinds: ELICITATION_FIELD_KINDS,
|
|
@@ -251,6 +258,10 @@ export const KIT_LOGIN_REPORT_PATH = "/kit/logins/report";
|
|
|
251
258
|
export const GUIDED_LOGIN_TIMEOUT_MS = 10 * 60 * 1000;
|
|
252
259
|
export const GUIDED_LOGIN_OUTPUT_MAX_BYTES = 64 * 1024;
|
|
253
260
|
|
|
261
|
+
export const KIT_SOURCES_DESIRED_PATH = "/kit/sources/desired";
|
|
262
|
+
export const KIT_SOURCES_REPORT_PATH = "/kit/sources/report";
|
|
263
|
+
export const SOURCE_CAPTURE_INTERVAL_MS = 15 * 60 * 1000;
|
|
264
|
+
|
|
254
265
|
export const EXECUTION_AUTHORITIES = ["allow", "refuse"];
|
|
255
266
|
|
|
256
267
|
export const PERMISSION_OPTION_KINDS = Object.freeze({
|
|
@@ -265,16 +276,21 @@ export const KIT_WORKSPACES_ACQUIRE_PATH = "/kit/workspaces/acquire";
|
|
|
265
276
|
export const WORKSPACE_ACQUIRE_MAX = 10;
|
|
266
277
|
export const WORKSPACE_POLL_MS = 2_000;
|
|
267
278
|
export const WORKSPACE_LABEL = "house-kit.workspace";
|
|
268
|
-
export const
|
|
269
|
-
export const WORKSPACE_KIT_HOME = "/workspace/.house-kit";
|
|
279
|
+
export const WORKSPACE_KIT_HOME = "/kit-home";
|
|
270
280
|
export const WORKSPACE_DISK_IMAGE = "disk.img";
|
|
271
281
|
export const WORKSPACE_DISK_MOUNT = "mnt";
|
|
282
|
+
export const WORKSPACE_PREVIOUS_SUFFIX = "-previous";
|
|
283
|
+
export const WORKSPACE_DISK_KIT_HOME = "kit-home";
|
|
284
|
+
export const WORKSPACE_DISK_HOUSE = "house";
|
|
285
|
+
export const WORKSPACE_SELECTED_IMAGE = "/opt/house-kit/image";
|
|
286
|
+
export const WORKSPACE_RELEASE_IMAGE = /^ghcr\.io\/agentshouse\/kit@sha256:[0-9a-f]{64}$/;
|
|
272
287
|
export const WORKSPACE_MOUNT_OPTIONS = "loop,nosuid,nodev";
|
|
273
288
|
export const HOST_MOUNT_INFO = "/proc/self/mountinfo";
|
|
274
289
|
export const WORKSPACE_SYSTEM_RESERVE_BYTES = 512 * 1024 * 1024;
|
|
275
290
|
export const WORKSPACE_COMMAND_TIMEOUT_MS = 120_000;
|
|
276
291
|
export const WORKSPACE_READY_ATTEMPTS = 60;
|
|
277
292
|
export const WORKSPACE_READY_POLL_MS = 1_000;
|
|
293
|
+
export const WORKSPACE_LOCK_POLL_MS = 1_000;
|
|
278
294
|
export const WORKSPACE_STOP_TIMEOUT_SECONDS = 10;
|
|
279
295
|
export const WORKSPACE_OPERATION_WITHDRAWN = "hosted_environment_operation_not_found";
|
|
280
296
|
export const CGROUP_CONTROLLERS = "/sys/fs/cgroup/cgroup.controllers";
|
|
@@ -288,9 +304,25 @@ export const KIT_DOOR_DISCOVER_PATH = "/kit/door/discover";
|
|
|
288
304
|
export const KIT_DOOR_BOOTSTRAP_PATH = "/kit/door/bootstrap";
|
|
289
305
|
export const KIT_DOOR_ENUMERATE_PATH = "/kit/door/enumerate";
|
|
290
306
|
export const KIT_DOOR_BATCH_PATH = "/kit/door/batch";
|
|
307
|
+
export const KIT_DOOR_REVISIONS_PATH = "/kit/door/revisions";
|
|
291
308
|
export const EXPLICIT_SYNC_VERSION = "explicit-sync/2";
|
|
292
309
|
export const KIT_DOOR_UNMOUNT_PATH = "/kit/door/unmount";
|
|
293
310
|
|
|
311
|
+
export const KIT_WORKING_COPY_SELECTION_PATH = "/kit/working-copy/selection";
|
|
312
|
+
export const WORKING_COPY_VERSION = "working-copy-state/3";
|
|
313
|
+
export const WORKING_COPIES_HOME = "working-copies";
|
|
314
|
+
export const WORKING_COPY_ROOM_HOMES = ["capture", "library", "collaboration"];
|
|
315
|
+
export const WORKING_COPY_PRIVATE_HOMES = ["library", "collaboration", "rooms", "agents", "house"];
|
|
316
|
+
export const WORKING_COPY_RECEIVED_REF = "refs/house/received";
|
|
317
|
+
export const WORKING_COPY_BRANCH = "main";
|
|
318
|
+
export const ROOM_REFERENCE = /^r_[0-9a-z]+\.[0-9a-z]$/;
|
|
319
|
+
export const WORKING_COPY_PUSH_METHOD = "house/git/push";
|
|
320
|
+
export const WORKING_COPY_INDEX_METHOD = "house/git/index";
|
|
321
|
+
export const WORKING_COPY_PUSH_HELP =
|
|
322
|
+
"Submit a Room commit to House; default HEAD. Use --owner only outside an Agent Session. Native git push is not a House remote.";
|
|
323
|
+
export const WORKING_COPY_UPLOAD_COMMAND = "prepare_edit_upload";
|
|
324
|
+
export const MCP_REQUEST_BODY_MAX_BYTES = 4 * 1024 * 1024;
|
|
325
|
+
|
|
294
326
|
export const MIGRATOR_CONTRACT = "kit-migrator/1";
|
|
295
327
|
export const MIGRATOR_CATEGORY = "migrator";
|
|
296
328
|
export const MIGRATOR_MANIFEST = "migrator.json";
|
|
@@ -43,9 +43,7 @@ export async function holdConversationFile(context, file, bytes) {
|
|
|
43
43
|
const path = statePath(context.directory, identity);
|
|
44
44
|
const noted = await readNote(path, null);
|
|
45
45
|
const observed = bytes === null || oversized ? null : describedBytes(bytes);
|
|
46
|
-
|
|
47
|
-
// every later one, so a file held again keeps the descriptor its note carries.
|
|
48
|
-
// Bytes that no longer answer that descriptor are not this original's bytes.
|
|
46
|
+
|
|
49
47
|
const descriptor = noted?.descriptor ?? observed;
|
|
50
48
|
const beyondBound = descriptor === null && (oversized || noted?.exceeded === true);
|
|
51
49
|
const held = {
|
package/src/delivery.js
CHANGED
|
@@ -54,10 +54,8 @@ const FAILURE_CATEGORIES = {
|
|
|
54
54
|
google_provider_unavailable: "provider",
|
|
55
55
|
google_provider_response_invalid: "provider",
|
|
56
56
|
google_checkpoint_invalid: "provider",
|
|
57
|
-
google_service_unsupported: "config",
|
|
58
|
-
google_oauth_client_unconfigured: "config",
|
|
59
|
-
google_drive_selection_exceeded: "config",
|
|
60
57
|
google_drive_item_missing: "provider",
|
|
58
|
+
api_disabled: "config",
|
|
61
59
|
};
|
|
62
60
|
|
|
63
61
|
export function fileDescriptor(file) {
|
|
@@ -184,8 +182,7 @@ export function stallOf(code, houseRetry = null) {
|
|
|
184
182
|
|
|
185
183
|
export function healthOfRun(stall) {
|
|
186
184
|
if (stall === null) return { state: "ok", category: null };
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
};
|
|
185
|
+
const category = stall.category ?? FAILURE_CATEGORIES[stall.code] ?? "internal";
|
|
186
|
+
if (stall.disposition === "retry") return { state: "retrying", category };
|
|
187
|
+
return { state: "failed", category, cause: stall.code };
|
|
191
188
|
}
|
package/src/directory.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
|
-
import { access, mkdir, realpath,
|
|
3
|
-
import {
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { access, mkdir, realpath, stat } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
5
4
|
import { LAUNCH_DIRECTORY_BASE } from "./contract.js";
|
|
6
5
|
import { KitRefusal } from "./refusal.js";
|
|
7
|
-
import { seedStarterSet } from "./starter-set.js";
|
|
8
6
|
|
|
9
7
|
export function launchBase(dependencies = {}) {
|
|
10
8
|
return dependencies.launchBase ?? LAUNCH_DIRECTORY_BASE;
|
|
@@ -39,25 +37,9 @@ function unavailable(path, cause) {
|
|
|
39
37
|
return new KitRefusal("kit_launch_directory_unavailable", `${path} ${cause}`);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
async function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} catch {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function seedDefaultDirectory(selected, base, path) {
|
|
51
|
-
const staging = join(base, `.${randomUUID()}.staging`);
|
|
52
|
-
try {
|
|
53
|
-
await seedStarterSet(staging);
|
|
54
|
-
await rename(staging, path);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
await rm(staging, { recursive: true, force: true }).catch(() => undefined);
|
|
57
|
-
if (!(await isDirectory(path))) {
|
|
58
|
-
throw unavailable(selected, `could not be prepared (${error.code ?? "unknown"})`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
40
|
+
async function createDefaultDirectory(selected, path) {
|
|
41
|
+
const refused = await made(path);
|
|
42
|
+
if (refused !== null) throw unavailable(selected, `could not be created (${refused})`);
|
|
61
43
|
}
|
|
62
44
|
|
|
63
45
|
function within(base, selected) {
|
|
@@ -73,9 +55,7 @@ export async function readyLaunchDirectory(selected, base, agentId = null, optio
|
|
|
73
55
|
if (options.container && !within(resolve(base), path)) {
|
|
74
56
|
throw unavailable(selected, `resolves outside ${base}`);
|
|
75
57
|
}
|
|
76
|
-
if (path === defaultDirectory(base, agentId)
|
|
77
|
-
await seedDefaultDirectory(selected, base, path);
|
|
78
|
-
}
|
|
58
|
+
if (path === defaultDirectory(base, agentId)) await createDefaultDirectory(selected, path);
|
|
79
59
|
let described;
|
|
80
60
|
try {
|
|
81
61
|
described = await stat(path);
|
package/src/draft.js
CHANGED
|
@@ -1,89 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const DRAFT_INTERVAL_MS = 500;
|
|
1
|
+
import { pacedLatest } from "./pacing.js";
|
|
4
2
|
|
|
5
3
|
export function observedAssistantDrafts(options) {
|
|
6
|
-
const
|
|
7
|
-
const now = options.now ?? (() => Date.now());
|
|
8
|
-
let timer = null;
|
|
9
|
-
let sentAt = null;
|
|
10
|
-
let sent = null;
|
|
11
|
-
let latest = null;
|
|
12
|
-
|
|
13
|
-
function write() {
|
|
14
|
-
timer = null;
|
|
15
|
-
if (latest === null || latest === sent) return;
|
|
16
|
-
sent = latest;
|
|
17
|
-
sentAt = now();
|
|
18
|
-
options.send(sent);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function cancel() {
|
|
22
|
-
if (timer === null) return;
|
|
23
|
-
clearTimeout(timer);
|
|
24
|
-
timer = null;
|
|
25
|
-
}
|
|
26
|
-
|
|
4
|
+
const paced = pacedLatest(options);
|
|
27
5
|
return {
|
|
28
6
|
observe(view) {
|
|
29
|
-
if (typeof view
|
|
30
|
-
latest = view;
|
|
31
|
-
if (timer !== null) return;
|
|
32
|
-
const wait = sentAt === null ? 0 : sentAt + intervalMs - now();
|
|
33
|
-
if (wait <= 0) {
|
|
34
|
-
write();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
timer = setTimeout(write, wait);
|
|
38
|
-
timer.unref?.();
|
|
39
|
-
},
|
|
40
|
-
complete(view) {
|
|
41
|
-
if (typeof view !== "string" || view === "") return;
|
|
42
|
-
latest = view;
|
|
43
|
-
cancel();
|
|
44
|
-
write();
|
|
45
|
-
},
|
|
46
|
-
close: cancel,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function openAssistantDrafts(options) {
|
|
51
|
-
let pending = null;
|
|
52
|
-
let sending = null;
|
|
53
|
-
let sent = null;
|
|
54
|
-
let closed = false;
|
|
55
|
-
|
|
56
|
-
function pump() {
|
|
57
|
-
if (sending !== null || pending === null) return;
|
|
58
|
-
const view = pending;
|
|
59
|
-
pending = null;
|
|
60
|
-
sent = view.text;
|
|
61
|
-
let started;
|
|
62
|
-
try {
|
|
63
|
-
started = Promise.resolve(options.send(view.blocks));
|
|
64
|
-
} catch {
|
|
65
|
-
started = Promise.resolve();
|
|
66
|
-
}
|
|
67
|
-
sending = started
|
|
68
|
-
.catch(() => undefined)
|
|
69
|
-
.finally(() => {
|
|
70
|
-
sending = null;
|
|
71
|
-
pump();
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
update(text) {
|
|
77
|
-
if (closed || typeof text !== "string" || text === "") return;
|
|
78
|
-
if (text === sent || text === pending?.text) return;
|
|
79
|
-
const blocks = assistantBlocks(text);
|
|
80
|
-
if (blocks === null || !withinAssistantBound(blocksText(blocks))) return;
|
|
81
|
-
pending = { text, blocks };
|
|
82
|
-
pump();
|
|
83
|
-
},
|
|
84
|
-
close() {
|
|
85
|
-
closed = true;
|
|
86
|
-
pump();
|
|
7
|
+
if (typeof view === "string" && view !== "") paced.observe(view);
|
|
87
8
|
},
|
|
9
|
+
close: paced.close,
|
|
88
10
|
};
|
|
89
11
|
}
|
package/src/git-main.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { accessSync, constants, realpathSync, statSync } from "node:fs";
|
|
3
|
+
import { constants as system } from "node:os";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import {
|
|
7
|
+
BRIDGE_NONCE_ENVIRONMENT,
|
|
8
|
+
BRIDGE_SOCKET_ENVIRONMENT,
|
|
9
|
+
WORKING_COPY_INDEX_METHOD,
|
|
10
|
+
WORKING_COPY_RECEIVED_REF,
|
|
11
|
+
} from "./contract.js";
|
|
12
|
+
import { bridgeCall } from "./mcp.js";
|
|
13
|
+
import { formatCommandError, KitRefusal } from "./refusal.js";
|
|
14
|
+
|
|
15
|
+
const SHIM = realpathSync(fileURLToPath(new URL("session-bin/git", import.meta.url)));
|
|
16
|
+
const VALUED = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--config-env", "--attr-source", "--super-prefix"]);
|
|
17
|
+
const RELAYED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
|
|
18
|
+
|
|
19
|
+
function nativeGit() {
|
|
20
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
21
|
+
if (directory === "") continue;
|
|
22
|
+
const candidate = join(directory, "git");
|
|
23
|
+
try {
|
|
24
|
+
if (!statSync(candidate).isFile() || realpathSync(candidate) === SHIM) continue;
|
|
25
|
+
accessSync(candidate, constants.X_OK);
|
|
26
|
+
return candidate;
|
|
27
|
+
} catch {}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function command(args) {
|
|
33
|
+
let index = 0;
|
|
34
|
+
while (index < args.length && args[index].startsWith("-")) index += VALUED.has(args[index]) ? 2 : 1;
|
|
35
|
+
return { globals: args.slice(0, index), name: args[index], rest: args.slice(index + 1) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function delegated(git, args) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const child = spawn(git, args, { stdio: "inherit" });
|
|
41
|
+
const relay = (signal) => child.kill(signal);
|
|
42
|
+
for (const signal of RELAYED) process.on(signal, relay);
|
|
43
|
+
const settle = (outcome) => {
|
|
44
|
+
for (const signal of RELAYED) process.off(signal, relay);
|
|
45
|
+
resolve(outcome);
|
|
46
|
+
};
|
|
47
|
+
child.once("error", (error) => {
|
|
48
|
+
process.stderr.write(`kit_git_unavailable: ${error.message}\n`);
|
|
49
|
+
settle({ status: 127, signal: null });
|
|
50
|
+
});
|
|
51
|
+
child.once("close", (status, signal) => settle({ status, signal }));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function captured(git, args, env = process.env) {
|
|
56
|
+
const run = spawnSync(git, args, { stdio: ["ignore", "pipe", "ignore"], maxBuffer: Infinity, env });
|
|
57
|
+
return run.status === 0 ? run.stdout.toString("utf8") : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function indexed(listing) {
|
|
61
|
+
const entries = new Map();
|
|
62
|
+
for (const record of listing.split("\0")) {
|
|
63
|
+
const tab = record.indexOf("\t");
|
|
64
|
+
if (tab < 0) continue;
|
|
65
|
+
const [mode, object, stage] = record.slice(0, tab).split(" ");
|
|
66
|
+
entries.set(record.slice(tab + 1), stage === "0" ? `${mode} ${object}` : null);
|
|
67
|
+
}
|
|
68
|
+
return entries;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function renamings(output) {
|
|
72
|
+
return (output ?? "").split("\n").filter((line) => line.startsWith("Renaming ")).map((line) => line.slice(9));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function movedPaths(renaming, before, after) {
|
|
76
|
+
for (let at = renaming.indexOf(" to "); at >= 0; at = renaming.indexOf(" to ", at + 1)) {
|
|
77
|
+
const from = renaming.slice(0, at);
|
|
78
|
+
const to = renaming.slice(at + 4);
|
|
79
|
+
if (before.has(from) && !after.has(from) && after.get(to) === before.get(from)) return [{ from, to }];
|
|
80
|
+
}
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function recorded(cwd, change) {
|
|
85
|
+
const socket = process.env[BRIDGE_SOCKET_ENVIRONMENT];
|
|
86
|
+
const nonce = process.env[BRIDGE_NONCE_ENVIRONMENT];
|
|
87
|
+
if (socket === undefined || nonce === undefined) return;
|
|
88
|
+
try {
|
|
89
|
+
const frame = await bridgeCall({ socket, nonce }, { method: WORKING_COPY_INDEX_METHOD, params: { cwd, ...change } });
|
|
90
|
+
if (frame.error !== undefined) throw new KitRefusal(frame.error.message ?? "kit_house_refused");
|
|
91
|
+
} catch (refusal) {
|
|
92
|
+
process.stderr.write(`${formatCommandError(refusal)}: house git push will not see this index change\n`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function observed(git, { globals, name, rest }, args) {
|
|
97
|
+
const located = captured(git, [...globals, "rev-parse", "--show-toplevel", "--show-cdup", WORKING_COPY_RECEIVED_REF, "HEAD"]);
|
|
98
|
+
if (located === null) return delegated(git, args);
|
|
99
|
+
const [top, cdup, , head] = located.split("\n");
|
|
100
|
+
const listing = [...globals, "ls-files", "--stage", "-z", "--full-name", "--", cdup === "" ? "." : cdup];
|
|
101
|
+
const before = captured(git, listing);
|
|
102
|
+
const rehearsal = [...globals, "mv", "--dry-run", ...rest];
|
|
103
|
+
const planned = name === "mv" ? renamings(captured(git, rehearsal, { ...process.env, LC_ALL: "C" })) : [];
|
|
104
|
+
const outcome = await delegated(git, args);
|
|
105
|
+
const after = before === null ? null : captured(git, listing);
|
|
106
|
+
if (after === null) return outcome;
|
|
107
|
+
const [prior, next] = [indexed(before), indexed(after)];
|
|
108
|
+
const moves = outcome.status === 0 ? planned.flatMap((renaming) => movedPaths(renaming, prior, next)) : [];
|
|
109
|
+
const sources = new Set(moves.map((move) => move.from));
|
|
110
|
+
const destinations = new Set(moves.map((move) => move.to));
|
|
111
|
+
const gone = [...prior.keys()].filter((path) => !next.has(path) && !sources.has(path));
|
|
112
|
+
const arrived = [...next.keys()].filter((path) => !prior.has(path) && !destinations.has(path));
|
|
113
|
+
const heads = captured(git, [...globals, "rev-parse", "HEAD", ...(name === "rebase" ? ["ORIG_HEAD"] : [])]);
|
|
114
|
+
const [current, tip = head] = heads?.trim().split("\n") ?? [];
|
|
115
|
+
const commit = current === undefined || current === head ? [] : [tip, current];
|
|
116
|
+
if (moves.length > 0 || gone.length > 0 || arrived.length > 0 || commit.length > 0) {
|
|
117
|
+
await recorded(top, { moves, gone, arrived, commit, command: name });
|
|
118
|
+
}
|
|
119
|
+
return outcome;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function run(args) {
|
|
123
|
+
const git = nativeGit();
|
|
124
|
+
if (git === null) {
|
|
125
|
+
process.stderr.write("kit_git_unavailable\n");
|
|
126
|
+
return { status: 127, signal: null };
|
|
127
|
+
}
|
|
128
|
+
return observed(git, command(args), args);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
run(process.argv.slice(2)).then(({ status, signal }) => {
|
|
132
|
+
if (signal === null) {
|
|
133
|
+
process.exitCode = status;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
process.exitCode = 128 + system.signals[signal];
|
|
137
|
+
process.kill(process.pid, signal);
|
|
138
|
+
});
|
package/src/git.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { KitRefusal } from "./refusal.js";
|
|
3
|
+
|
|
4
|
+
function cancelledOr(signal, error, code) {
|
|
5
|
+
return signal?.aborted === true || error?.name === "AbortError"
|
|
6
|
+
? new KitRefusal("kit_command_cancelled")
|
|
7
|
+
: new KitRefusal(code);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function stdio(capture, input) {
|
|
11
|
+
if (input !== null) return ["pipe", capture ? "pipe" : "inherit", "inherit"];
|
|
12
|
+
return capture ? ["ignore", "pipe", "inherit"] : "inherit";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function runGit(cwd, args, options = {}) {
|
|
16
|
+
const { dependencies = {}, capture = false, input = null, signal, failure } = options;
|
|
17
|
+
if (signal?.aborted === true) return Promise.reject(new KitRefusal("kit_command_cancelled"));
|
|
18
|
+
const launcher = dependencies.spawn ?? spawn;
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
let child;
|
|
21
|
+
try {
|
|
22
|
+
child = launcher("git", args, {
|
|
23
|
+
cwd,
|
|
24
|
+
env: process.env,
|
|
25
|
+
stdio: stdio(capture, input),
|
|
26
|
+
signal,
|
|
27
|
+
killSignal: "SIGKILL",
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
reject(cancelledOr(signal, error, "kit_git_unavailable"));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const output = [];
|
|
34
|
+
let settled = false;
|
|
35
|
+
const finish = (operation) => {
|
|
36
|
+
if (settled) return;
|
|
37
|
+
settled = true;
|
|
38
|
+
operation();
|
|
39
|
+
};
|
|
40
|
+
child.stdout?.on("data", (chunk) => output.push(Buffer.from(chunk)));
|
|
41
|
+
child.once("error", (error) =>
|
|
42
|
+
finish(() => reject(cancelledOr(signal, error, "kit_git_unavailable"))),
|
|
43
|
+
);
|
|
44
|
+
child.once("close", (status) => {
|
|
45
|
+
finish(() => {
|
|
46
|
+
if (signal?.aborted === true) {
|
|
47
|
+
reject(new KitRefusal("kit_command_cancelled"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (status !== 0) {
|
|
51
|
+
reject(new KitRefusal(failure));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
resolve(Buffer.concat(output));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
if (input !== null) {
|
|
58
|
+
child.stdin.on("error", () => undefined);
|
|
59
|
+
child.stdin.end(input);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
package/src/google/calendar.js
CHANGED
|
@@ -4,9 +4,10 @@ import { googleJson } from "./gateway.js";
|
|
|
4
4
|
import { GOOGLE_SCOPES } from "./oauth.js";
|
|
5
5
|
|
|
6
6
|
const CALENDAR_ORIGIN = "https://www.googleapis.com";
|
|
7
|
+
const CALENDAR = "primary";
|
|
7
8
|
|
|
8
|
-
function cursorOf(value
|
|
9
|
-
if (value === null) return {
|
|
9
|
+
function cursorOf(value) {
|
|
10
|
+
if (value === null) return { pageToken: null, syncToken: null };
|
|
10
11
|
let cursor;
|
|
11
12
|
try {
|
|
12
13
|
cursor = JSON.parse(value);
|
|
@@ -16,16 +17,8 @@ function cursorOf(value, calendars) {
|
|
|
16
17
|
if (
|
|
17
18
|
typeof cursor !== "object" ||
|
|
18
19
|
cursor === null ||
|
|
19
|
-
!Number.isInteger(cursor.calendar) ||
|
|
20
|
-
cursor.calendar < 0 ||
|
|
21
|
-
cursor.calendar >= calendars.length ||
|
|
22
20
|
(cursor.pageToken !== null && typeof cursor.pageToken !== "string") ||
|
|
23
|
-
typeof cursor.
|
|
24
|
-
cursor.syncTokens === null ||
|
|
25
|
-
Array.isArray(cursor.syncTokens) ||
|
|
26
|
-
Object.entries(cursor.syncTokens).some(
|
|
27
|
-
([calendar, token]) => !calendars.includes(calendar) || typeof token !== "string",
|
|
28
|
-
)
|
|
21
|
+
(cursor.syncToken !== null && typeof cursor.syncToken !== "string")
|
|
29
22
|
) {
|
|
30
23
|
throw new KitRefusal("google_checkpoint_invalid", "calendar");
|
|
31
24
|
}
|
|
@@ -45,7 +38,7 @@ function authorOf(event) {
|
|
|
45
38
|
: event.organizer.email;
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
function normalizedEvent(event
|
|
41
|
+
function normalizedEvent(event) {
|
|
49
42
|
if (
|
|
50
43
|
typeof event?.id !== "string" ||
|
|
51
44
|
typeof event.etag !== "string" ||
|
|
@@ -71,45 +64,53 @@ function normalizedEvent(event, calendar) {
|
|
|
71
64
|
author: authorOf(event),
|
|
72
65
|
url: event.htmlLink ?? null,
|
|
73
66
|
attachments: [],
|
|
74
|
-
container: `google/calendar/${
|
|
67
|
+
container: `google/calendar/${CALENDAR}`,
|
|
75
68
|
};
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
function common(options) {
|
|
72
|
+
return {
|
|
73
|
+
directory: options.directory,
|
|
74
|
+
account: options.account,
|
|
75
|
+
requiredScopes: [GOOGLE_SCOPES.calendar],
|
|
76
|
+
signal: options.signal,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function pageUrl(cursor, limit, backfillFloor) {
|
|
81
|
+
const url = new URL(`/calendar/v3/calendars/${CALENDAR}/events`, CALENDAR_ORIGIN);
|
|
83
82
|
url.searchParams.set("maxResults", `${limit}`);
|
|
84
83
|
url.searchParams.set("singleEvents", "true");
|
|
85
84
|
url.searchParams.set("showDeleted", "true");
|
|
86
85
|
if (cursor.pageToken !== null) url.searchParams.set("pageToken", cursor.pageToken);
|
|
87
|
-
|
|
88
|
-
if (syncToken !== undefined) url.searchParams.set("syncToken", syncToken);
|
|
86
|
+
if (cursor.syncToken !== null) url.searchParams.set("syncToken", cursor.syncToken);
|
|
89
87
|
else if (backfillFloor !== null && backfillFloor !== undefined) {
|
|
90
88
|
url.searchParams.set("timeMin", backfillFloor);
|
|
91
89
|
}
|
|
92
90
|
return url.toString();
|
|
93
91
|
}
|
|
94
92
|
|
|
93
|
+
export async function calendarEmail(options, dependencies = {}) {
|
|
94
|
+
const calendar = await googleJson(
|
|
95
|
+
{ ...common(options), url: `${CALENDAR_ORIGIN}/calendar/v3/calendars/${CALENDAR}` },
|
|
96
|
+
dependencies,
|
|
97
|
+
);
|
|
98
|
+
if (typeof calendar.id !== "string") {
|
|
99
|
+
throw new KitRefusal("google_provider_response_invalid", "calendar");
|
|
100
|
+
}
|
|
101
|
+
return calendar.id;
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
export async function calendarPage(options, dependencies = {}) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const calendar = calendars[cursor.calendar];
|
|
99
|
-
const common = {
|
|
100
|
-
directory: options.directory,
|
|
101
|
-
instance: options.instance,
|
|
102
|
-
client: options.client,
|
|
103
|
-
requiredScopes: [GOOGLE_SCOPES.calendar],
|
|
104
|
-
signal: options.signal,
|
|
105
|
-
};
|
|
105
|
+
let cursor = cursorOf(options.cursor);
|
|
106
|
+
const shared = common(options);
|
|
106
107
|
let answer;
|
|
107
108
|
try {
|
|
108
109
|
answer = await googleJson(
|
|
109
110
|
{
|
|
110
|
-
...
|
|
111
|
+
...shared,
|
|
111
112
|
goneCode: "google_calendar_cursor_expired",
|
|
112
|
-
url: pageUrl(
|
|
113
|
+
url: pageUrl(cursor, options.limit, options.backfillFloor),
|
|
113
114
|
},
|
|
114
115
|
dependencies,
|
|
115
116
|
);
|
|
@@ -117,11 +118,9 @@ export async function calendarPage(options, dependencies = {}) {
|
|
|
117
118
|
if (!(error instanceof KitRefusal) || error.code !== "google_calendar_cursor_expired") {
|
|
118
119
|
throw error;
|
|
119
120
|
}
|
|
120
|
-
|
|
121
|
-
delete syncTokens[calendar];
|
|
122
|
-
cursor = { calendar: cursor.calendar, pageToken: null, syncTokens };
|
|
121
|
+
cursor = { pageToken: null, syncToken: null };
|
|
123
122
|
answer = await googleJson(
|
|
124
|
-
{ ...
|
|
123
|
+
{ ...shared, url: pageUrl(cursor, options.limit, options.backfillFloor) },
|
|
125
124
|
dependencies,
|
|
126
125
|
);
|
|
127
126
|
}
|
|
@@ -135,23 +134,18 @@ export async function calendarPage(options, dependencies = {}) {
|
|
|
135
134
|
}
|
|
136
135
|
let next;
|
|
137
136
|
if (answer.nextPageToken !== undefined) {
|
|
138
|
-
next = {
|
|
137
|
+
next = { pageToken: answer.nextPageToken, syncToken: cursor.syncToken };
|
|
139
138
|
} else {
|
|
140
139
|
if (typeof answer.nextSyncToken !== "string") {
|
|
141
140
|
throw new KitRefusal("google_provider_response_invalid", "calendar sync token");
|
|
142
141
|
}
|
|
143
|
-
|
|
144
|
-
next = {
|
|
145
|
-
calendar: cursor.calendar + 1 === calendars.length ? 0 : cursor.calendar + 1,
|
|
146
|
-
pageToken: null,
|
|
147
|
-
syncTokens,
|
|
148
|
-
};
|
|
142
|
+
next = { pageToken: null, syncToken: answer.nextSyncToken };
|
|
149
143
|
}
|
|
150
144
|
const normalized = [];
|
|
151
145
|
const dropped = [];
|
|
152
146
|
for (const event of items) {
|
|
153
147
|
try {
|
|
154
|
-
normalized.push(normalizedEvent(event
|
|
148
|
+
normalized.push(normalizedEvent(event));
|
|
155
149
|
} catch (error) {
|
|
156
150
|
if (
|
|
157
151
|
error instanceof KitRefusal &&
|
|
@@ -172,6 +166,6 @@ export async function calendarPage(options, dependencies = {}) {
|
|
|
172
166
|
items: normalized,
|
|
173
167
|
dropped,
|
|
174
168
|
cursor: JSON.stringify(next),
|
|
175
|
-
more: answer.nextPageToken !== undefined
|
|
169
|
+
more: answer.nextPageToken !== undefined,
|
|
176
170
|
};
|
|
177
171
|
}
|