@agentshouse/kit 0.1.0-alpha.25 → 0.1.0-alpha.27
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 +172 -158
- package/bin/connect-linux.sh +15 -4
- package/bin/kit.js +48 -46
- package/bootstrap.json +3 -3
- package/package.json +1 -1
- package/src/adapter-mcp.js +23 -1
- package/src/bridge.js +4 -0
- package/src/configuration.js +13 -209
- package/src/connector-login.js +3 -84
- package/src/contract.js +4 -0
- package/src/delivery.js +4 -7
- package/src/directory.js +6 -26
- package/src/google/calendar.js +39 -45
- package/src/google/drive.js +61 -186
- package/src/google/entries.js +346 -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/house.js +43 -5
- package/src/how-we-work.md +25 -0
- package/src/kit-work-stream.js +1 -1
- package/src/launcher.js +11 -4
- package/src/mcp.js +5 -0
- package/src/setup.js +8 -14
- 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 +51 -26
- package/src/state.js +19 -1
- package/src/tasks.js +72 -0
- package/src/worker.js +46 -68
- package/src/working-copy.js +1 -15
- 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/tasks.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
|
|
3
|
+
export function coalescedTask(signal, fail, work) {
|
|
4
|
+
let pending = false;
|
|
5
|
+
let task = null;
|
|
6
|
+
const trigger = () => {
|
|
7
|
+
pending = true;
|
|
8
|
+
if (task !== null) return;
|
|
9
|
+
task = (async () => {
|
|
10
|
+
while (pending && !signal.aborted) {
|
|
11
|
+
pending = false;
|
|
12
|
+
await work();
|
|
13
|
+
}
|
|
14
|
+
})()
|
|
15
|
+
.catch((error) => {
|
|
16
|
+
if (signal.aborted) return;
|
|
17
|
+
fail(error);
|
|
18
|
+
})
|
|
19
|
+
.finally(() => {
|
|
20
|
+
task = null;
|
|
21
|
+
if (pending && !signal.aborted) trigger();
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
return { trigger, idle: () => task === null, settled: () => task };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function residentWake() {
|
|
28
|
+
let pending = false;
|
|
29
|
+
let release = null;
|
|
30
|
+
return {
|
|
31
|
+
notify() {
|
|
32
|
+
pending = true;
|
|
33
|
+
release?.();
|
|
34
|
+
},
|
|
35
|
+
async wait(milliseconds, signal) {
|
|
36
|
+
if (pending) {
|
|
37
|
+
pending = false;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const pause = new AbortController();
|
|
41
|
+
try {
|
|
42
|
+
await Promise.race([
|
|
43
|
+
new Promise((resolve) => {
|
|
44
|
+
release = resolve;
|
|
45
|
+
}),
|
|
46
|
+
delay(milliseconds, undefined, { signal: AbortSignal.any([signal, pause.signal]) }),
|
|
47
|
+
]);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (!signal.aborted) throw error;
|
|
50
|
+
} finally {
|
|
51
|
+
release = null;
|
|
52
|
+
pending = false;
|
|
53
|
+
pause.abort();
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const turns = new Map();
|
|
60
|
+
|
|
61
|
+
export function exclusively(path, work) {
|
|
62
|
+
const turn = (turns.get(path) ?? Promise.resolve()).then(work);
|
|
63
|
+
const settled = turn.then(
|
|
64
|
+
() => undefined,
|
|
65
|
+
() => undefined,
|
|
66
|
+
);
|
|
67
|
+
turns.set(path, settled);
|
|
68
|
+
void settled.then(() => {
|
|
69
|
+
if (turns.get(path) === settled) turns.delete(path);
|
|
70
|
+
});
|
|
71
|
+
return turn;
|
|
72
|
+
}
|
package/src/worker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { setTimeout as delay } from "node:timers/promises";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -17,9 +18,11 @@ import { retryConversationFiles } from "./conversation-originals.js";
|
|
|
17
18
|
import { openAssistantDrafts } from "./draft.js";
|
|
18
19
|
import { localRequest, reconcileInbound } from "./inbound.js";
|
|
19
20
|
import { localFiles } from "./local-files.js";
|
|
20
|
-
import { answerText, answerYaml } from "./mcp.js";
|
|
21
|
+
import { answerText, answerYaml, editRefusal } from "./mcp.js";
|
|
21
22
|
import { inheritedEnvironment } from "./command.js";
|
|
23
|
+
import { sourceEntries } from "./google/entries.js";
|
|
22
24
|
import { watchKitWork } from "./kit-work-stream.js";
|
|
25
|
+
import { coalescedTask, residentWake } from "./tasks.js";
|
|
23
26
|
import {
|
|
24
27
|
applyWorkingCopies,
|
|
25
28
|
deliverWorkingCopies,
|
|
@@ -84,6 +87,7 @@ import {
|
|
|
84
87
|
import { operationId } from "./operation.js";
|
|
85
88
|
import { runSetup, signInWantedAgent } from "./setup.js";
|
|
86
89
|
import { readSettlements, removeSettlement, writeSettlement } from "./settlement.js";
|
|
90
|
+
import { howWeWorkOwed, noteHowWeWork, reconcileSkillSet } from "./skill-set.js";
|
|
87
91
|
import { writeText } from "./state.js";
|
|
88
92
|
import { failedReport, terminalReport } from "./terminal.js";
|
|
89
93
|
import {
|
|
@@ -285,13 +289,12 @@ function endOnEpochReport(ended, act) {
|
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
function refusedWith(text) {
|
|
288
|
-
const conflicts =
|
|
289
|
-
const current =
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return { refused: { code: text.split(":")[0].trim(), text, current } };
|
|
292
|
+
const { code, conflicts } = editRefusal(text);
|
|
293
|
+
const current =
|
|
294
|
+
conflicts?.map(({ path, state, revision }) =>
|
|
295
|
+
state === "absent" ? { path, revision: null, content: null } : { path, revision },
|
|
296
|
+
) ?? null;
|
|
297
|
+
return { refused: { code, text, current } };
|
|
295
298
|
}
|
|
296
299
|
|
|
297
300
|
function acceptedWith(text) {
|
|
@@ -389,6 +392,10 @@ async function openRunBridge(context, bound, authority, start) {
|
|
|
389
392
|
start,
|
|
390
393
|
question: handed((batch) => admitInteraction(context.session, bound.session, batch)),
|
|
391
394
|
plan: handed((snapshot) => admitPlanSnapshot(context.session, bound.session, snapshot)),
|
|
395
|
+
howWeWork: () =>
|
|
396
|
+
noteHowWeWork(context.directory).catch((error) =>
|
|
397
|
+
reported(`How-we-work note unwritable (${error.code ?? "unknown"})`),
|
|
398
|
+
),
|
|
392
399
|
interrupt: handed((interrupt, outcome) =>
|
|
393
400
|
reportInterruptOutcome(context.session, interrupt, outcome),
|
|
394
401
|
),
|
|
@@ -576,6 +583,7 @@ async function prepareAssignment(context, assignment, resumed) {
|
|
|
576
583
|
const authority = { token: assignment.session.token, expiresAt: assignment.session.expiresAt };
|
|
577
584
|
const start = {
|
|
578
585
|
base_instructions: assignment.baseInstructions,
|
|
586
|
+
create_how_we_work: await howWeWorkOwed(context.directory),
|
|
579
587
|
working_directory: assignment.workingDirectory,
|
|
580
588
|
working_copies: await appliedWorkingCopies(context),
|
|
581
589
|
execution_authority: context.configuration.executionAuthority,
|
|
@@ -855,6 +863,7 @@ async function workerContext(options, dependencies, resident = false) {
|
|
|
855
863
|
launchBase: launchBase(dependencies),
|
|
856
864
|
container: dependencies.container ?? process.env.HOUSE_KIT_CONTAINER === "1",
|
|
857
865
|
directory: options.directory,
|
|
866
|
+
home: dependencies.home ?? homedir(),
|
|
858
867
|
files: { originals: [], failures: [] },
|
|
859
868
|
epochReport: null,
|
|
860
869
|
agentRefresh: Promise.resolve(),
|
|
@@ -903,6 +912,11 @@ function queueAgentRefresh(context, dependencies, signal, configuration, pointer
|
|
|
903
912
|
async function controlledPass(context, dependencies, configuration, signal) {
|
|
904
913
|
context.signal = signal;
|
|
905
914
|
context.session.signal = signal;
|
|
915
|
+
await reconcileSkillSet({
|
|
916
|
+
directory: context.directory,
|
|
917
|
+
home: context.home,
|
|
918
|
+
accepted: configuration.skills,
|
|
919
|
+
});
|
|
906
920
|
context.busy = true;
|
|
907
921
|
try {
|
|
908
922
|
return await afterEpochReport(context, () => workerPass(context, dependencies, configuration));
|
|
@@ -943,62 +957,6 @@ async function reportedWorkingCopies(act, context, signal) {
|
|
|
943
957
|
}
|
|
944
958
|
}
|
|
945
959
|
|
|
946
|
-
function coalescedTask(signal, fail, work) {
|
|
947
|
-
let pending = false;
|
|
948
|
-
let task = null;
|
|
949
|
-
const trigger = () => {
|
|
950
|
-
pending = true;
|
|
951
|
-
if (task !== null) return;
|
|
952
|
-
task = (async () => {
|
|
953
|
-
while (pending && !signal.aborted) {
|
|
954
|
-
pending = false;
|
|
955
|
-
await work();
|
|
956
|
-
}
|
|
957
|
-
})()
|
|
958
|
-
.catch((error) => {
|
|
959
|
-
if (signal.aborted) return;
|
|
960
|
-
fail(error);
|
|
961
|
-
})
|
|
962
|
-
.finally(() => {
|
|
963
|
-
task = null;
|
|
964
|
-
if (pending && !signal.aborted) trigger();
|
|
965
|
-
});
|
|
966
|
-
};
|
|
967
|
-
return { trigger, idle: () => task === null, settled: () => task };
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
function residentWake() {
|
|
971
|
-
let pending = false;
|
|
972
|
-
let release = null;
|
|
973
|
-
return {
|
|
974
|
-
notify() {
|
|
975
|
-
pending = true;
|
|
976
|
-
release?.();
|
|
977
|
-
},
|
|
978
|
-
async wait(milliseconds, signal) {
|
|
979
|
-
if (pending) {
|
|
980
|
-
pending = false;
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
const pause = new AbortController();
|
|
984
|
-
try {
|
|
985
|
-
await Promise.race([
|
|
986
|
-
new Promise((resolve) => {
|
|
987
|
-
release = resolve;
|
|
988
|
-
}),
|
|
989
|
-
delay(milliseconds, undefined, { signal: AbortSignal.any([signal, pause.signal]) }),
|
|
990
|
-
]);
|
|
991
|
-
} catch (error) {
|
|
992
|
-
if (!signal.aborted) throw error;
|
|
993
|
-
} finally {
|
|
994
|
-
release = null;
|
|
995
|
-
pending = false;
|
|
996
|
-
pause.abort();
|
|
997
|
-
}
|
|
998
|
-
},
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
960
|
function residentUpdate(context, wake) {
|
|
1003
961
|
let requested = existsSync(updateDrain(context.directory));
|
|
1004
962
|
let ready = null;
|
|
@@ -1066,6 +1024,7 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1066
1024
|
const wake = residentWake();
|
|
1067
1025
|
const update = residentUpdate(context, wake);
|
|
1068
1026
|
context.update = update;
|
|
1027
|
+
let sources = null;
|
|
1069
1028
|
const served = await serveResident(
|
|
1070
1029
|
session,
|
|
1071
1030
|
context.directory,
|
|
@@ -1075,6 +1034,8 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1075
1034
|
pushWorkingCopy({ directory: context.directory, session }, request, ownerCaller(session), signal).catch(
|
|
1076
1035
|
(error) => ({ refused: true, text: formatCommandError(error) }),
|
|
1077
1036
|
),
|
|
1037
|
+
(source) =>
|
|
1038
|
+
sources === null ? Promise.reject(new KitRefusal("kit_resident_unavailable")) : sources.capture(source),
|
|
1078
1039
|
);
|
|
1079
1040
|
const serve = async (signal) => {
|
|
1080
1041
|
context.busy = true;
|
|
@@ -1137,13 +1098,24 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1137
1098
|
const deliveries = coalescedTask(signal, unavailable, () =>
|
|
1138
1099
|
reportedWorkingCopies(deliverWorkingCopies, context, signal),
|
|
1139
1100
|
);
|
|
1101
|
+
sources = sourceEntries({
|
|
1102
|
+
session: context.session,
|
|
1103
|
+
directory: context.directory,
|
|
1104
|
+
dependencies,
|
|
1105
|
+
signal: AbortSignal.any([signal, watcher.signal]),
|
|
1106
|
+
now: session.now,
|
|
1107
|
+
draining: () => update.requested,
|
|
1108
|
+
unavailable: (error) => reported(`google sources unavailable: ${error?.message ?? error}`),
|
|
1109
|
+
});
|
|
1110
|
+
await sources.start();
|
|
1140
1111
|
const pointer = () => {
|
|
1141
1112
|
if (!update.requested) agentRefresh.trigger();
|
|
1142
1113
|
};
|
|
1143
|
-
const available = (subject) => {
|
|
1114
|
+
const available = (subject = null) => {
|
|
1144
1115
|
if (update.requested) return;
|
|
1145
|
-
if (subject
|
|
1146
|
-
if (subject
|
|
1116
|
+
if (subject === null || subject === "agents") agentRefresh.trigger();
|
|
1117
|
+
if (subject === null || subject === "working_copy") copies.trigger();
|
|
1118
|
+
if (subject === null || subject === "sources") sources.reconcile();
|
|
1147
1119
|
};
|
|
1148
1120
|
context.onIdle = pointer;
|
|
1149
1121
|
context.workingCopiesApplied = copies.settled;
|
|
@@ -1168,7 +1140,12 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1168
1140
|
if (sessions.length > 0) update.answer({ ready: false, sessions });
|
|
1169
1141
|
else if (
|
|
1170
1142
|
session.refused ||
|
|
1171
|
-
(!served.serving() &&
|
|
1143
|
+
(!served.serving() &&
|
|
1144
|
+
logins.size === 0 &&
|
|
1145
|
+
agentRefresh.idle() &&
|
|
1146
|
+
copies.idle() &&
|
|
1147
|
+
deliveries.idle() &&
|
|
1148
|
+
sources.idle())
|
|
1172
1149
|
) {
|
|
1173
1150
|
return;
|
|
1174
1151
|
}
|
|
@@ -1182,6 +1159,7 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1182
1159
|
await agentRefresh.settled();
|
|
1183
1160
|
await copies.settled();
|
|
1184
1161
|
await deliveries.settled();
|
|
1162
|
+
await sources.settled();
|
|
1185
1163
|
for (const running of logins.values()) running.controller.abort();
|
|
1186
1164
|
await Promise.all([...logins.values()].map((running) => running.task));
|
|
1187
1165
|
await context.agentRefresh;
|
package/src/working-copy.js
CHANGED
|
@@ -23,6 +23,7 @@ import { readNote, writeNote, writeText } from "./state.js";
|
|
|
23
23
|
import { bootstrapBundle, discoveryOf, door, withSession } from "./sync.js";
|
|
24
24
|
import { isGeneratedProvenance, sidecarPath, structuredOrReplace } from "./sync-library.js";
|
|
25
25
|
import { locate, removeReplicaFile, writeReplicaFile } from "./sync-state.js";
|
|
26
|
+
import { exclusively } from "./tasks.js";
|
|
26
27
|
|
|
27
28
|
const COMMITTER_NAME = "House Kit";
|
|
28
29
|
const COMMITTER_EMAIL = "house-kit@localhost";
|
|
@@ -366,21 +367,6 @@ async function heldCopies(directory) {
|
|
|
366
367
|
return held.filter((copy) => copy !== null);
|
|
367
368
|
}
|
|
368
369
|
|
|
369
|
-
const turns = new Map();
|
|
370
|
-
|
|
371
|
-
function exclusively(path, work) {
|
|
372
|
-
const turn = (turns.get(path) ?? Promise.resolve()).then(work);
|
|
373
|
-
const settled = turn.then(
|
|
374
|
-
() => undefined,
|
|
375
|
-
() => undefined,
|
|
376
|
-
);
|
|
377
|
-
turns.set(path, settled);
|
|
378
|
-
void settled.then(() => {
|
|
379
|
-
if (turns.get(path) === settled) turns.delete(path);
|
|
380
|
-
});
|
|
381
|
-
return turn;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
370
|
function received(scope, read, parents) {
|
|
385
371
|
const files = read.files.filter((file) => file.writable);
|
|
386
372
|
return { message: `House ${housePath(scope)} at position ${read.position}`, parents, files, whole: true };
|
package/src/google/declared.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { ITEM_BODY_MAX_BYTES } from "../contract.js";
|
|
2
|
-
import { KitRefusal } from "../refusal.js";
|
|
3
|
-
import { googleJson } from "./gateway.js";
|
|
4
|
-
|
|
5
|
-
function valueAt(value, path) {
|
|
6
|
-
let current = value;
|
|
7
|
-
for (const segment of path.split(".")) {
|
|
8
|
-
if (current === null || current === undefined || typeof current !== "object") return undefined;
|
|
9
|
-
current = current[segment];
|
|
10
|
-
}
|
|
11
|
-
return current;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function requiredText(value, declaration) {
|
|
15
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
16
|
-
throw new KitRefusal("google_provider_response_invalid", declaration);
|
|
17
|
-
}
|
|
18
|
-
return value;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function optionalText(value, declaration) {
|
|
22
|
-
if (value === undefined || value === null) return null;
|
|
23
|
-
return requiredText(value, declaration);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function cursorOf(value) {
|
|
27
|
-
if (value === null) return { pageToken: null, checkpoint: null };
|
|
28
|
-
let cursor;
|
|
29
|
-
try {
|
|
30
|
-
cursor = JSON.parse(value);
|
|
31
|
-
} catch {
|
|
32
|
-
throw new KitRefusal("google_checkpoint_invalid", "declared service");
|
|
33
|
-
}
|
|
34
|
-
if (
|
|
35
|
-
typeof cursor !== "object" ||
|
|
36
|
-
cursor === null ||
|
|
37
|
-
(cursor.pageToken !== null && typeof cursor.pageToken !== "string") ||
|
|
38
|
-
(cursor.checkpoint !== null && typeof cursor.checkpoint !== "string")
|
|
39
|
-
) {
|
|
40
|
-
throw new KitRefusal("google_checkpoint_invalid", "declared service");
|
|
41
|
-
}
|
|
42
|
-
return cursor;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function composedBody(item, paths) {
|
|
46
|
-
const parts = paths.map((path) => valueAt(item, path));
|
|
47
|
-
if (parts.some((part) => !["string", "number", "boolean"].includes(typeof part))) {
|
|
48
|
-
throw new KitRefusal("google_provider_response_invalid", "declared body");
|
|
49
|
-
}
|
|
50
|
-
const body = parts.map((part) => `${part}`).join("\n\n");
|
|
51
|
-
if (Buffer.byteLength(body, "utf8") > ITEM_BODY_MAX_BYTES) {
|
|
52
|
-
throw new KitRefusal("google_provider_response_invalid", "declared body");
|
|
53
|
-
}
|
|
54
|
-
return body;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function normalizedItem(service, item) {
|
|
58
|
-
const fields = service.declaration.fields;
|
|
59
|
-
const observedAt = requiredText(valueAt(item, fields.observedAt), "declared observed time");
|
|
60
|
-
if (Number.isNaN(Date.parse(observedAt))) {
|
|
61
|
-
throw new KitRefusal("google_provider_response_invalid", "declared observed time");
|
|
62
|
-
}
|
|
63
|
-
const container = requiredText(valueAt(item, fields.container), "declared container");
|
|
64
|
-
const nativeVersion =
|
|
65
|
-
fields.nativeVersion === null
|
|
66
|
-
? null
|
|
67
|
-
: optionalText(valueAt(item, fields.nativeVersion), "declared native version");
|
|
68
|
-
const providerModifiedAt =
|
|
69
|
-
fields.providerModifiedAt === null
|
|
70
|
-
? null
|
|
71
|
-
: optionalText(
|
|
72
|
-
valueAt(item, fields.providerModifiedAt),
|
|
73
|
-
"declared provider modification time",
|
|
74
|
-
);
|
|
75
|
-
if (providerModifiedAt !== null && Number.isNaN(Date.parse(providerModifiedAt))) {
|
|
76
|
-
throw new KitRefusal("google_provider_response_invalid", "declared provider modification time");
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
namespace: `google/${service.service}`,
|
|
80
|
-
nativeObjectId: requiredText(valueAt(item, fields.nativeId), "declared native id"),
|
|
81
|
-
nativeVersion,
|
|
82
|
-
providerModifiedAt:
|
|
83
|
-
providerModifiedAt === null ? null : new Date(providerModifiedAt).toISOString(),
|
|
84
|
-
observedAt: new Date(observedAt).toISOString(),
|
|
85
|
-
body: composedBody(item, fields.body),
|
|
86
|
-
author:
|
|
87
|
-
fields.author === null
|
|
88
|
-
? null
|
|
89
|
-
: optionalText(valueAt(item, fields.author), "declared author"),
|
|
90
|
-
url:
|
|
91
|
-
fields.url === null ? null : optionalText(valueAt(item, fields.url), "declared url"),
|
|
92
|
-
attachments: [],
|
|
93
|
-
container: `google/${service.service}/${container}`,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export async function declaredPage(options, dependencies = {}) {
|
|
98
|
-
const declared = options.service.declaration;
|
|
99
|
-
const cursor = cursorOf(options.cursor);
|
|
100
|
-
const url = new URL(declared.path, `https://${declared.host}`);
|
|
101
|
-
for (const [name, value] of Object.entries(declared.query)) url.searchParams.set(name, value);
|
|
102
|
-
url.searchParams.set(declared.pageSize, `${options.limit}`);
|
|
103
|
-
if (cursor.pageToken !== null) {
|
|
104
|
-
url.searchParams.set(declared.pageToken.request, cursor.pageToken);
|
|
105
|
-
}
|
|
106
|
-
if (cursor.checkpoint !== null) {
|
|
107
|
-
url.searchParams.set(declared.pageToken.checkpointRequest, cursor.checkpoint);
|
|
108
|
-
}
|
|
109
|
-
const answer = await googleJson(
|
|
110
|
-
{
|
|
111
|
-
directory: options.directory,
|
|
112
|
-
instance: options.instance,
|
|
113
|
-
client: options.client,
|
|
114
|
-
requiredScopes: declared.scopes,
|
|
115
|
-
url: url.toString(),
|
|
116
|
-
signal: options.signal,
|
|
117
|
-
},
|
|
118
|
-
dependencies,
|
|
119
|
-
);
|
|
120
|
-
const entries = valueAt(answer, declared.items);
|
|
121
|
-
if (!Array.isArray(entries) || entries.length > options.limit) {
|
|
122
|
-
throw new KitRefusal("google_provider_response_invalid", "declared items");
|
|
123
|
-
}
|
|
124
|
-
const pageToken = valueAt(answer, declared.pageToken.response);
|
|
125
|
-
let next;
|
|
126
|
-
if (pageToken !== undefined && pageToken !== null) {
|
|
127
|
-
next = {
|
|
128
|
-
pageToken: requiredText(pageToken, "declared page token"),
|
|
129
|
-
checkpoint: cursor.checkpoint,
|
|
130
|
-
};
|
|
131
|
-
} else {
|
|
132
|
-
next = {
|
|
133
|
-
pageToken: null,
|
|
134
|
-
checkpoint: requiredText(
|
|
135
|
-
valueAt(answer, declared.pageToken.checkpointResponse),
|
|
136
|
-
"declared checkpoint",
|
|
137
|
-
),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
items: entries.map((entry) => normalizedItem(options.service, entry)),
|
|
142
|
-
dropped: [],
|
|
143
|
-
cursor: JSON.stringify(next),
|
|
144
|
-
more: pageToken !== undefined && pageToken !== null,
|
|
145
|
-
};
|
|
146
|
-
}
|
package/src/google/id.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const GOOGLE_DRIVE_ID = /^[A-Za-z0-9_-]{1,512}$/;
|
package/src/instance/google.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const GOOGLE_OAUTH_CLIENT = Object.freeze({"id":"911965338518-867gdvuddb7kdkjo3qvpv6gds369alb7.apps.googleusercontent.com","secret":"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"});
|
package/src/starter-set.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { cp, mkdir, symlink, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const AGENTS_INSTRUCTION = "The agents.house `house` CLI is available: run `house --help`.\n";
|
|
6
|
-
const GRILLING_SOURCE = fileURLToPath(new URL("starter-set/grilling/", import.meta.url));
|
|
7
|
-
|
|
8
|
-
export async function seedStarterSet(target) {
|
|
9
|
-
await mkdir(target, { recursive: true });
|
|
10
|
-
await writeFile(join(target, "AGENTS.md"), AGENTS_INSTRUCTION);
|
|
11
|
-
await symlink("AGENTS.md", join(target, "CLAUDE.md"));
|
|
12
|
-
const skills = join(target, ".agents", "skills");
|
|
13
|
-
await mkdir(skills, { recursive: true });
|
|
14
|
-
await cp(GRILLING_SOURCE, join(skills, "grilling"), { recursive: true });
|
|
15
|
-
await mkdir(join(target, ".claude"), { recursive: true });
|
|
16
|
-
await symlink(join("..", ".agents", "skills"), join(target, ".claude", "skills"));
|
|
17
|
-
}
|
|
File without changes
|
|
File without changes
|