typeclaw 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +15 -13
  2. package/auth.schema.json +41 -0
  3. package/cron.schema.json +8 -0
  4. package/package.json +1 -1
  5. package/secrets.schema.json +41 -0
  6. package/src/agent/auth.ts +45 -22
  7. package/src/agent/index.ts +189 -19
  8. package/src/agent/multimodal/index.ts +12 -0
  9. package/src/agent/multimodal/look-at.ts +185 -0
  10. package/src/agent/multimodal/looker.ts +145 -0
  11. package/src/agent/plugin-tools.ts +30 -1
  12. package/src/agent/session-origin.ts +194 -46
  13. package/src/agent/subagents.ts +57 -1
  14. package/src/agent/system-prompt.ts +1 -1
  15. package/src/agent/tool-result-budget.ts +121 -0
  16. package/src/bundled-plugins/backup/index.ts +23 -8
  17. package/src/bundled-plugins/backup/runner.ts +22 -0
  18. package/src/bundled-plugins/memory/README.md +13 -10
  19. package/src/bundled-plugins/memory/append-tool.ts +87 -61
  20. package/src/bundled-plugins/memory/dreaming.ts +137 -7
  21. package/src/bundled-plugins/memory/find-entry-tool.ts +62 -0
  22. package/src/bundled-plugins/memory/fragment-parser.ts +19 -44
  23. package/src/bundled-plugins/memory/index.ts +91 -8
  24. package/src/bundled-plugins/memory/load-memory.ts +74 -34
  25. package/src/bundled-plugins/memory/memory-logger.ts +72 -29
  26. package/src/bundled-plugins/memory/migration.ts +276 -0
  27. package/src/bundled-plugins/memory/stream-events.ts +55 -0
  28. package/src/bundled-plugins/memory/stream-io.ts +63 -0
  29. package/src/bundled-plugins/memory/watermark.ts +48 -8
  30. package/src/bundled-plugins/security/index.ts +103 -10
  31. package/src/bundled-plugins/security/permissions.ts +12 -0
  32. package/src/bundled-plugins/security/policies/git-exfil.ts +51 -18
  33. package/src/bundled-plugins/tool-result-cap/README.md +9 -4
  34. package/src/bundled-plugins/tool-result-cap/cap-jsonl.ts +115 -0
  35. package/src/bundled-plugins/tool-result-cap/cap-result.ts +25 -13
  36. package/src/bundled-plugins/tool-result-cap/index.ts +16 -2
  37. package/src/channels/adapters/discord-bot-classify.ts +2 -6
  38. package/src/channels/adapters/discord-bot.ts +4 -45
  39. package/src/channels/adapters/kakaotalk-classify.ts +3 -7
  40. package/src/channels/adapters/kakaotalk.ts +28 -47
  41. package/src/channels/adapters/slack-bot-classify.ts +2 -6
  42. package/src/channels/adapters/slack-bot.ts +4 -50
  43. package/src/channels/adapters/telegram-bot-classify.ts +8 -10
  44. package/src/channels/adapters/telegram-bot.ts +3 -16
  45. package/src/channels/index.ts +3 -2
  46. package/src/channels/manager.ts +15 -1
  47. package/src/channels/persistence.ts +44 -10
  48. package/src/channels/router.ts +228 -19
  49. package/src/channels/schema.ts +6 -156
  50. package/src/cli/channel.ts +200 -4
  51. package/src/cli/compose-usage.ts +182 -0
  52. package/src/cli/compose.ts +33 -0
  53. package/src/cli/hostd.ts +49 -1
  54. package/src/cli/index.ts +4 -0
  55. package/src/cli/init.ts +809 -300
  56. package/src/cli/model.ts +244 -0
  57. package/src/cli/provider.ts +404 -0
  58. package/src/cli/reload.ts +11 -3
  59. package/src/cli/role.ts +156 -0
  60. package/src/cli/run.ts +3 -1
  61. package/src/cli/tui.ts +13 -3
  62. package/src/cli/usage-args.ts +47 -0
  63. package/src/cli/usage.ts +97 -0
  64. package/src/compose/index.ts +1 -0
  65. package/src/compose/usage.ts +65 -0
  66. package/src/config/config.ts +491 -19
  67. package/src/config/index.ts +15 -1
  68. package/src/config/models-mutation.ts +200 -0
  69. package/src/config/providers-mutation.ts +250 -0
  70. package/src/config/providers.ts +141 -2
  71. package/src/config/reloadable.ts +15 -4
  72. package/src/container/index.ts +6 -1
  73. package/src/container/port.ts +10 -0
  74. package/src/container/require-running.ts +33 -0
  75. package/src/container/start.ts +81 -63
  76. package/src/cron/consumer.ts +22 -2
  77. package/src/cron/index.ts +45 -4
  78. package/src/cron/schema.ts +104 -0
  79. package/src/doctor/checks.ts +51 -34
  80. package/src/doctor/plugin-bridge.ts +28 -4
  81. package/src/git/system-commit.ts +103 -0
  82. package/src/hostd/daemon.ts +16 -0
  83. package/src/hostd/kakao-renewal-manager.ts +223 -0
  84. package/src/hostd/paths.ts +7 -0
  85. package/src/init/dockerfile.ts +36 -10
  86. package/src/init/gitignore.ts +1 -1
  87. package/src/init/index.ts +213 -85
  88. package/src/init/kakaotalk-auth.ts +18 -1
  89. package/src/init/models-dev.ts +26 -1
  90. package/src/init/run-owner-claim.ts +77 -0
  91. package/src/permissions/builtins.ts +70 -0
  92. package/src/permissions/grant.ts +99 -0
  93. package/src/permissions/index.ts +29 -0
  94. package/src/permissions/match-rule.ts +305 -0
  95. package/src/permissions/permissions.ts +196 -0
  96. package/src/permissions/resolve.ts +80 -0
  97. package/src/permissions/schema.ts +79 -0
  98. package/src/plugin/context.ts +8 -4
  99. package/src/plugin/define.ts +2 -0
  100. package/src/plugin/index.ts +2 -0
  101. package/src/plugin/manager.ts +41 -0
  102. package/src/plugin/registry.ts +9 -0
  103. package/src/plugin/types.ts +35 -1
  104. package/src/reload/client.ts +25 -1
  105. package/src/role-claim/client.ts +182 -0
  106. package/src/role-claim/code.ts +53 -0
  107. package/src/role-claim/controller.ts +194 -0
  108. package/src/role-claim/index.ts +19 -0
  109. package/src/role-claim/match-rule.ts +43 -0
  110. package/src/role-claim/pending.ts +100 -0
  111. package/src/run/channel-session-factory.ts +76 -5
  112. package/src/run/index.ts +68 -7
  113. package/src/secrets/encryption.ts +116 -0
  114. package/src/secrets/kakao-renewal.ts +248 -0
  115. package/src/secrets/kakao-store.ts +66 -7
  116. package/src/secrets/keys.ts +173 -0
  117. package/src/secrets/schema.ts +23 -0
  118. package/src/secrets/storage.ts +83 -0
  119. package/src/server/index.ts +198 -71
  120. package/src/shared/index.ts +4 -0
  121. package/src/shared/protocol.ts +27 -0
  122. package/src/skills/typeclaw-channel-kakaotalk/SKILL.md +3 -3
  123. package/src/skills/typeclaw-config/SKILL.md +104 -112
  124. package/src/skills/typeclaw-memory/SKILL.md +9 -9
  125. package/src/skills/typeclaw-permissions/SKILL.md +166 -0
  126. package/src/stream/types.ts +7 -1
  127. package/src/tui/client.ts +66 -5
  128. package/src/tui/index.ts +61 -9
  129. package/src/usage/aggregate.ts +117 -0
  130. package/src/usage/format.ts +30 -0
  131. package/src/usage/index.ts +68 -0
  132. package/src/usage/report.ts +354 -0
  133. package/src/usage/scan.ts +186 -0
  134. package/typeclaw.schema.json +134 -98
package/README.md CHANGED
@@ -43,7 +43,7 @@ The bundled `memory` plugin turns lived experience into reusable knowledge. No m
43
43
 
44
44
  1. **Observe.** After every idle turn, a `memory-logger` subagent reads the transcript and appends notable fragments to `memory/yyyy-MM-dd.md`. Cheap, frequent, lossy by design.
45
45
  2. **Dream.** On a cron schedule (default 4am), a `dreaming` subagent consolidates daily streams into `MEMORY.md`, and — when it spots a procedure worth remembering — writes it as **muscle memory**: a new skill at `memory/skills/<name>/SKILL.md`.
46
- 3. **Apply.** Tomorrow's prompt sees the updated `MEMORY.md`. Muscle-memory skills sit alongside bundled and user-installed ones, loaded on demand. Every dream is `git commit -m Dream`'d, so growth is auditable.
46
+ 3. **Apply.** Tomorrow's prompt sees the updated `MEMORY.md`. Muscle-memory skills sit alongside bundled and user-installed ones, loaded on demand. Every dream is committed with a one-line summary — e.g. `dream: 3 fragments + new skill 'pr-review' 🔮` — so growth is auditable.
47
47
 
48
48
  See [`src/bundled-plugins/memory/README.md`](./src/bundled-plugins/memory/README.md) for the full contract.
49
49
 
@@ -68,18 +68,20 @@ That's it. The agent is now alive, listening on a websocket, ready to receive pr
68
68
 
69
69
  ## CLI
70
70
 
71
- | Command | Purpose |
72
- | ------------------ | -------------------------------------------------------------------- |
73
- | `typeclaw init` | Scaffold a new agent folder |
74
- | `typeclaw start` | Build and run the container |
75
- | `typeclaw stop` | Stop the container |
76
- | `typeclaw restart` | `stop` then `start` |
77
- | `typeclaw status` | Show container + daemon registration state |
78
- | `typeclaw logs` | Stream container stdout/stderr with local timestamps; `-f` to follow |
79
- | `typeclaw tui` | Attach a terminal UI over the agent's websocket |
80
- | `typeclaw shell` | Open a shell inside the running container |
81
- | `typeclaw reload` | Push a live config reload to the running agent |
82
- | `typeclaw compose` | Orchestrate multiple agents |
71
+ | Command | Purpose |
72
+ | ----------------------------------- | ---------------------------------------------------------------------------------- |
73
+ | `typeclaw init` | Scaffold a new agent folder |
74
+ | `typeclaw start` | Build and run the container |
75
+ | `typeclaw stop` | Stop the container |
76
+ | `typeclaw restart` | `stop` then `start` |
77
+ | `typeclaw status` | Show container + daemon registration state |
78
+ | `typeclaw logs` | Stream container stdout/stderr with local timestamps; `-f` to follow |
79
+ | `typeclaw tui` | Attach a terminal UI over the agent's websocket |
80
+ | `typeclaw shell` | Open a shell inside the running container |
81
+ | `typeclaw reload` | Push a live config reload to the running agent |
82
+ | `typeclaw compose` | Orchestrate multiple agents |
83
+ | `typeclaw channel add <kind>` | Wire a new channel adapter (Slack, Discord, Telegram, KakaoTalk) |
84
+ | `typeclaw channel reauth kakaotalk` | Re-authenticate KakaoTalk after a stale-token 401 or to rotate the stored password |
83
85
 
84
86
  ## Configuration
85
87
 
package/auth.schema.json CHANGED
@@ -233,6 +233,47 @@
233
233
  },
234
234
  "updated_at": {
235
235
  "type": "string"
236
+ },
237
+ "email": {
238
+ "type": "string"
239
+ },
240
+ "encryptedPassword": {
241
+ "type": "object",
242
+ "properties": {
243
+ "v": {
244
+ "type": "number",
245
+ "const": 1
246
+ },
247
+ "alg": {
248
+ "type": "string",
249
+ "const": "AES-256-GCM"
250
+ },
251
+ "kid": {
252
+ "type": "string"
253
+ },
254
+ "iv": {
255
+ "type": "string"
256
+ },
257
+ "ciphertext": {
258
+ "type": "string"
259
+ },
260
+ "authTag": {
261
+ "type": "string"
262
+ },
263
+ "createdAt": {
264
+ "type": "string"
265
+ }
266
+ },
267
+ "required": [
268
+ "v",
269
+ "alg",
270
+ "kid",
271
+ "iv",
272
+ "ciphertext",
273
+ "authTag",
274
+ "createdAt"
275
+ ],
276
+ "additionalProperties": false
236
277
  }
237
278
  },
238
279
  "required": [
package/cron.schema.json CHANGED
@@ -29,6 +29,10 @@
29
29
  "timezone": {
30
30
  "type": "string"
31
31
  },
32
+ "scheduledByRole": {
33
+ "type": "string"
34
+ },
35
+ "scheduledByOrigin": {},
32
36
  "kind": {
33
37
  "type": "string",
34
38
  "const": "prompt"
@@ -69,6 +73,10 @@
69
73
  "timezone": {
70
74
  "type": "string"
71
75
  },
76
+ "scheduledByRole": {
77
+ "type": "string"
78
+ },
79
+ "scheduledByOrigin": {},
72
80
  "kind": {
73
81
  "type": "string",
74
82
  "const": "exec"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typeclaw",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "homepage": "https://github.com/typeclaw/typeclaw#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/typeclaw/typeclaw/issues"
@@ -233,6 +233,47 @@
233
233
  },
234
234
  "updated_at": {
235
235
  "type": "string"
236
+ },
237
+ "email": {
238
+ "type": "string"
239
+ },
240
+ "encryptedPassword": {
241
+ "type": "object",
242
+ "properties": {
243
+ "v": {
244
+ "type": "number",
245
+ "const": 1
246
+ },
247
+ "alg": {
248
+ "type": "string",
249
+ "const": "AES-256-GCM"
250
+ },
251
+ "kid": {
252
+ "type": "string"
253
+ },
254
+ "iv": {
255
+ "type": "string"
256
+ },
257
+ "ciphertext": {
258
+ "type": "string"
259
+ },
260
+ "authTag": {
261
+ "type": "string"
262
+ },
263
+ "createdAt": {
264
+ "type": "string"
265
+ }
266
+ },
267
+ "required": [
268
+ "v",
269
+ "alg",
270
+ "kid",
271
+ "iv",
272
+ "ciphertext",
273
+ "authTag",
274
+ "createdAt"
275
+ ],
276
+ "additionalProperties": false
236
277
  }
237
278
  },
238
279
  "required": [
package/src/agent/auth.ts CHANGED
@@ -23,12 +23,18 @@ function secretsJsonPath(): string {
23
23
  return join(process.cwd(), 'secrets.json')
24
24
  }
25
25
 
26
- let cached: Auth | null = null
26
+ // Per-provider cache. Sessions that use a profile mapped to provider X share
27
+ // a single AuthStorage + ModelRegistry for that provider; first use of a new
28
+ // provider lazily resolves its credentials. This replaces the singleton
29
+ // `getAuth()` from before multi-model — the singleton couldn't represent
30
+ // "auth for `default` is OpenAI, auth for `vision` is Fireworks" without
31
+ // constructing both at boot.
32
+ const cached = new Map<KnownProviderId, Auth>()
33
+
34
+ export function getAuthFor(providerId: KnownProviderId): Auth {
35
+ const existing = cached.get(providerId)
36
+ if (existing) return existing
27
37
 
28
- export function getAuth(): Auth {
29
- if (cached) return cached
30
-
31
- const providerId = providerForModelRef(getConfig().model)
32
38
  const provider = KNOWN_PROVIDERS[providerId]
33
39
 
34
40
  if (process.env.NODE_ENV === 'test' && !hasAnyCredentialInEnv(provider.apiKeyEnv)) {
@@ -37,8 +43,9 @@ export function getAuth(): Auth {
37
43
  authStorage.setRuntimeApiKey(provider.id, TEST_DUMMY_API_KEY)
38
44
  }
39
45
  const modelRegistry = ModelRegistry.create(authStorage)
40
- cached = { authStorage, modelRegistry }
41
- return cached
46
+ const auth = { authStorage, modelRegistry }
47
+ cached.set(providerId, auth)
48
+ return auth
42
49
  }
43
50
 
44
51
  const authStorage = createSecretsStoreForAgent(secretsJsonPath())
@@ -53,16 +60,11 @@ export function getAuth(): Auth {
53
60
  // is set. OAuth credentials in the file still take precedence on read
54
61
  // because AuthStorage's hasAuth checks runtimeOverrides first only for
55
62
  // api-key-shaped credentials — OAuth on disk wins on its own.
56
- //
57
- // The previous code branch that wrote the env value into secrets.json and
58
- // stripped the matching `.env` line has been removed. Env values stay in
59
- // env; the file stays user-owned. See src/secrets/hydrate.ts for the same
60
- // policy on the channels side.
61
63
  if (supportsApiKey(provider) && provider.apiKeyEnv) {
62
64
  const envKey = process.env[provider.apiKeyEnv]
63
65
  if (envKey !== undefined && envKey !== '') {
64
- const existing = authStorage.get(provider.id)
65
- if (existing === undefined || existing.type === 'api_key') {
66
+ const existingCred = authStorage.get(provider.id)
67
+ if (existingCred === undefined || existingCred.type === 'api_key') {
66
68
  authStorage.setRuntimeApiKey(provider.id, envKey)
67
69
  }
68
70
  }
@@ -74,12 +76,20 @@ export function getAuth(): Auth {
74
76
  }
75
77
 
76
78
  const modelRegistry = ModelRegistry.create(authStorage)
77
- cached = { authStorage, modelRegistry }
78
- return cached
79
+ const auth = { authStorage, modelRegistry }
80
+ cached.set(providerId, auth)
81
+ return auth
82
+ }
83
+
84
+ // Back-compat shim for callers that still want the `default` profile's auth
85
+ // (the main session path). Equivalent to `getAuthFor(provider-of-default)`.
86
+ export function getAuth(): Auth {
87
+ const defaultRef = getConfig().models.default
88
+ return getAuthFor(providerForModelRef(defaultRef))
79
89
  }
80
90
 
81
91
  export function resetAuthForTesting(): void {
82
- cached = null
92
+ cached.clear()
83
93
  }
84
94
 
85
95
  function hasAnyCredentialInEnv(apiKeyEnv: string | null): boolean {
@@ -88,19 +98,32 @@ function hasAnyCredentialInEnv(apiKeyEnv: string | null): boolean {
88
98
 
89
99
  function missingCredentialMessage(providerId: KnownProviderId): string {
90
100
  const provider = KNOWN_PROVIDERS[providerId]
91
- const ref = getConfig().model
92
- const slash = ref.indexOf('/')
101
+ const defaultRef = getConfig().models.default
102
+ const defaultProviderId = providerForModelRef(defaultRef)
103
+ // For the `default` profile, name the model in the error message (matches
104
+ // pre-multi-model behavior). For any other profile, the user is mixing
105
+ // providers across profiles and the error must name the failing provider
106
+ // without claiming it's tied to the `default` model.
107
+ const isDefault = defaultProviderId === providerId
108
+ const ref = isDefault ? defaultRef : null
93
109
  const modelName =
94
- (provider.models as Record<string, { name: string }>)[ref.slice(slash + 1)]?.name ?? ref.slice(slash + 1)
110
+ ref !== null
111
+ ? ((provider.models as Record<string, { name: string }>)[ref.slice(ref.indexOf('/') + 1)]?.name ??
112
+ ref.slice(ref.indexOf('/') + 1))
113
+ : null
95
114
 
96
115
  const oauthOnly = supportsOAuth(provider) && !supportsApiKey(provider)
97
116
  const apiKeyOnly = supportsApiKey(provider) && !supportsOAuth(provider)
98
117
 
99
118
  if (oauthOnly) {
100
- return `No credentials for ${provider.name}. Run \`typeclaw init\` and pick "OAuth" to log in to ${modelName}.`
119
+ return modelName
120
+ ? `No credentials for ${provider.name}. Run \`typeclaw init\` and pick "OAuth" to log in to ${modelName}.`
121
+ : `No credentials for ${provider.name} (referenced by a non-default profile). Run \`typeclaw init\` and pick "OAuth" to log in.`
101
122
  }
102
123
  if (apiKeyOnly && provider.apiKeyEnv) {
103
- return `Set ${provider.apiKeyEnv} in .env (or secrets.json#providers.${provider.id}.key.value) to use ${modelName} via ${provider.name}.`
124
+ return modelName
125
+ ? `Set ${provider.apiKeyEnv} in .env (or secrets.json#providers.${provider.id}.key.value) to use ${modelName} via ${provider.name}.`
126
+ : `Set ${provider.apiKeyEnv} in .env (or secrets.json#providers.${provider.id}.key.value) to use ${provider.name} (referenced by a non-default profile).`
104
127
  }
105
128
  return `No credentials for ${provider.name}. Either set ${provider.apiKeyEnv ?? '<api-key-env>'} in .env or run \`typeclaw init\` and pick "OAuth".`
106
129
  }