@hobin/developer 0.1.6 → 0.1.8
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 +127 -95
- package/SOURCES.md +8 -8
- package/extensions/developer.ts +1752 -1441
- package/extensions/machine.ts +418 -286
- package/extensions/references/behavior-preserving-structural-change.md +14 -13
- package/extensions/state.ts +463 -419
- package/extensions/tool-policy.ts +158 -65
- package/extensions/tui.ts +1666 -910
- package/package.json +1 -1
- package/skills/sketch/references/responsibility-and-variation.md +1 -1
|
@@ -1,94 +1,187 @@
|
|
|
1
|
-
|
|
1
|
+
export type ControlledToolCapability = "shell" | "artifact";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export const TOOL_POLICY_LIFECYCLE_ENTRY = "developer.tool-policy-lifecycle";
|
|
4
|
+
export const TOOL_POLICY_LIFECYCLE = "released-before-reload/v1";
|
|
4
5
|
|
|
5
|
-
const CONTROLLED_BUILTIN_CAPABILITIES = new Map<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const CONTROLLED_BUILTIN_CAPABILITIES = new Map<
|
|
7
|
+
string,
|
|
8
|
+
ControlledToolCapability
|
|
9
|
+
>([
|
|
10
|
+
["bash", "shell"],
|
|
11
|
+
["edit", "artifact"],
|
|
12
|
+
["write", "artifact"],
|
|
9
13
|
]);
|
|
10
14
|
|
|
11
15
|
export interface ToolMetadataLike {
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
name: string;
|
|
17
|
+
sourceInfo: { source: string };
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export interface ToolPolicyMemory {
|
|
17
|
-
|
|
21
|
+
withheldBuiltins: Set<string>;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export interface ToolPolicyResult {
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
activeTools: string[];
|
|
26
|
+
memory: ToolPolicyMemory;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
export interface ProtocolToolAccess {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
allowsShell: boolean;
|
|
31
|
+
allowsArtifactTools: boolean;
|
|
32
|
+
hasBeforeImplementationGate: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ToolPolicyBranchEntry {
|
|
36
|
+
type?: string;
|
|
37
|
+
customType?: string;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
message?: {
|
|
40
|
+
role?: string;
|
|
41
|
+
toolName?: string;
|
|
42
|
+
details?: unknown;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface ToolPolicyLifecycleMarker {
|
|
47
|
+
protocol: string;
|
|
48
|
+
kind: "tool-policy-lifecycle";
|
|
49
|
+
lifecycle: typeof TOOL_POLICY_LIFECYCLE;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
53
|
+
return typeof value === "object" && value !== null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function reloadSafeToolPolicyMarker(
|
|
57
|
+
protocol: string,
|
|
58
|
+
): ToolPolicyLifecycleMarker {
|
|
59
|
+
return {
|
|
60
|
+
protocol,
|
|
61
|
+
kind: "tool-policy-lifecycle",
|
|
62
|
+
lifecycle: TOOL_POLICY_LIFECYCLE,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isReloadSafeToolPolicyMarker(
|
|
67
|
+
entry: ToolPolicyBranchEntry,
|
|
68
|
+
protocol: string,
|
|
69
|
+
): boolean {
|
|
70
|
+
if (
|
|
71
|
+
entry.type !== "custom" ||
|
|
72
|
+
entry.customType !== TOOL_POLICY_LIFECYCLE_ENTRY ||
|
|
73
|
+
!isObject(entry.data)
|
|
74
|
+
)
|
|
75
|
+
return false;
|
|
76
|
+
return (
|
|
77
|
+
entry.data.protocol === protocol &&
|
|
78
|
+
entry.data.kind === "tool-policy-lifecycle" &&
|
|
79
|
+
entry.data.lifecycle === TOOL_POLICY_LIFECYCLE
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function toolPolicyReloadRequiresRestart(input: {
|
|
84
|
+
entries: readonly ToolPolicyBranchEntry[];
|
|
85
|
+
protocol: string;
|
|
86
|
+
protocolTools: readonly string[];
|
|
87
|
+
}): boolean {
|
|
88
|
+
const protocolTools = new Set(input.protocolTools);
|
|
89
|
+
for (let index = input.entries.length - 1; index >= 0; index -= 1) {
|
|
90
|
+
const entry = input.entries[index];
|
|
91
|
+
if (!entry) continue;
|
|
92
|
+
if (isReloadSafeToolPolicyMarker(entry, input.protocol)) return false;
|
|
93
|
+
|
|
94
|
+
let value: unknown;
|
|
95
|
+
if (
|
|
96
|
+
entry.type === "custom" &&
|
|
97
|
+
entry.customType?.startsWith("developer.") &&
|
|
98
|
+
entry.customType !== TOOL_POLICY_LIFECYCLE_ENTRY
|
|
99
|
+
) {
|
|
100
|
+
value = entry.data;
|
|
101
|
+
} else if (
|
|
102
|
+
entry.type === "message" &&
|
|
103
|
+
entry.message?.role === "toolResult" &&
|
|
104
|
+
entry.message.toolName &&
|
|
105
|
+
protocolTools.has(entry.message.toolName)
|
|
106
|
+
) {
|
|
107
|
+
value = entry.message.details;
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
isObject(value) &&
|
|
111
|
+
typeof value.protocol === "string" &&
|
|
112
|
+
value.protocol.startsWith("developer/")
|
|
113
|
+
)
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
29
117
|
}
|
|
30
118
|
|
|
31
119
|
export function builtinControlledToolCapabilities(
|
|
32
|
-
|
|
120
|
+
tools: ToolMetadataLike[],
|
|
33
121
|
): Map<string, ControlledToolCapability> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
122
|
+
const result = new Map<string, ControlledToolCapability>();
|
|
123
|
+
for (const tool of tools) {
|
|
124
|
+
if (tool.sourceInfo.source !== "builtin") continue;
|
|
125
|
+
const capability = CONTROLLED_BUILTIN_CAPABILITIES.get(tool.name);
|
|
126
|
+
if (capability) result.set(tool.name, capability);
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
41
129
|
}
|
|
42
130
|
|
|
43
131
|
export function isControlledToolAllowed(input: {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
132
|
+
enabled: boolean;
|
|
133
|
+
capability: ControlledToolCapability;
|
|
134
|
+
access: ProtocolToolAccess;
|
|
47
135
|
}): boolean {
|
|
48
|
-
|
|
136
|
+
if (!input.enabled) return true;
|
|
49
137
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
138
|
+
if (input.access.hasBeforeImplementationGate) {
|
|
139
|
+
if (input.capability === "artifact") return false;
|
|
140
|
+
return input.access.allowsShell && !input.access.allowsArtifactTools;
|
|
141
|
+
}
|
|
54
142
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return input.access.canMutate;
|
|
143
|
+
if (input.capability === "shell") return input.access.allowsShell;
|
|
144
|
+
return input.access.allowsArtifactTools;
|
|
58
145
|
}
|
|
59
146
|
|
|
60
147
|
export function reconcileProtocolTools(input: {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
148
|
+
activeTools: string[];
|
|
149
|
+
allTools: ToolMetadataLike[];
|
|
150
|
+
enabled: boolean;
|
|
151
|
+
access: ProtocolToolAccess;
|
|
152
|
+
protocolTools: readonly string[];
|
|
153
|
+
memory: ToolPolicyMemory;
|
|
67
154
|
}): ToolPolicyResult {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
155
|
+
const active = new Set(input.activeTools);
|
|
156
|
+
const controlledBuiltins = builtinControlledToolCapabilities(input.allTools);
|
|
157
|
+
const withheld = new Set(
|
|
158
|
+
[...input.memory.withheldBuiltins].filter((name) =>
|
|
159
|
+
controlledBuiltins.has(name),
|
|
160
|
+
),
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (!input.enabled) {
|
|
164
|
+
for (const name of withheld) active.add(name);
|
|
165
|
+
withheld.clear();
|
|
166
|
+
for (const tool of input.protocolTools) active.delete(tool);
|
|
167
|
+
} else {
|
|
168
|
+
for (const tool of input.protocolTools) active.add(tool);
|
|
169
|
+
for (const [name, capability] of controlledBuiltins) {
|
|
170
|
+
const allowed = isControlledToolAllowed({
|
|
171
|
+
enabled: input.enabled,
|
|
172
|
+
capability,
|
|
173
|
+
access: input.access,
|
|
174
|
+
});
|
|
175
|
+
if (allowed) {
|
|
176
|
+
if (withheld.delete(name)) active.add(name);
|
|
177
|
+
} else if (active.delete(name)) {
|
|
178
|
+
withheld.add(name);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
activeTools: [...active],
|
|
185
|
+
memory: { withheldBuiltins: withheld },
|
|
186
|
+
};
|
|
94
187
|
}
|